
Here is a simple summary of some programming mistakes that C++ beginners often make, providing a reference for newcomers.
1. Some keywords are unnecessarily repeated in the cpp file
For C++ classes, some keywords only need to be written in the .h file and do not need to be repeated in the cpp file, such as the keywords virtual and static. If they are repeated in the cpp file, the compiler will throw an error.
For example, the definitions of virtual interfaces and static member variables only need to be declared in the header file.
class shape
{
virtual Draw();
//...
static int nLevel;
}
2. Default values for function parameters are written in the function implementation
For functions with default parameter values, the default values should be added in the function declaration, not in the function implementation.
To make the code easier to read, the default values can be commented out in the function implementation. The correct approach is to have the default values in the header file:
BOOL CreateConf( const CString& strConfName, const BOOL bAudio = FALSE );
// In the function implementation, do not add default values:
BOOL CreateConf( const CString& strConfName, const BOOL bAudio/* = FALSE*/ );
{
// ......
}
3. Forgetting to add a semicolon at the end of a class definition
Forgetting to add a semicolon at the end of a class definition will cause a compilation error, and beginners may spend a long time trying to figure out the cause of the compilation error.
It’s actually quite simple; the semicolon was just forgotten at the end of the class definition.
class Shape
{
// ...
};
4. Only adding function declarations without implementations
When adding functions to a class, only the function declarations are added in the header file, but the implementations are missing in the cpp file.
If the function is called elsewhere, a compilation link error will occur, reporting<span><span>unresolved external symbol</span></span> error. This is because there is no implementation, and thus no obj file is available for linking.
5. Forgetting to add the cpp file to the project, resulting in no obj file generated for linking
When adding a C++ class, we generally add the .h header file and a .cpp source file. However, if we forget to add the .cpp file to the project, it will not be compiled, and no obj file will be generated for linking.
If there is code that calls the interface of this C++ class, a compilation link error will occur, reporting<span><span>unresolved external symbol</span></span> error, meaning the corresponding interface of the C++ class cannot be linked.
6. Returning the address or reference of a local variable from a function
Returning the address or reference of a local variable from a function will cause issues, as the local variable’s lifetime ends when the function exits, and the memory is released.
When external code accesses that variable’s memory, it will trigger a memory access violation exception because the memory for that variable has already been released. For example, the following erroneous code:
char* GetResult()
{
char chResult[100] = { 0 };
// ......
return chResult;
}
7. Forgetting to declare the parent class interface as a virtual function, causing polymorphism to fail
The code was intended to utilize C++ polymorphism through virtual function calls to invoke the child class’s implementation, but the corresponding interface in the parent class was not declared as virtual, resulting in the child class’s function not being called.
It is essential to remember that to implement function calls under polymorphism, the relevant interfaces in the parent class must be declared as virtual.
class Shape()
{
// ...
virtual void Draw();
// ...
}
8. Using a single pointer where a double pointer should be used
Sometimes we need to call an interface to retrieve certain data, and the interface copies the data into the memory corresponding to the passed parameters. In this case, we should pass a pointer or reference.
Before calling GetData, we defined a structure pointer p and allocated memory for the corresponding structure object. However, we mistakenly defined the GetData interface using a single pointer instead of a double pointer (pointer to pointer).
The problematic code is as follows:
struct CodecInfo // Codec information
{
int nFrameRate;
// ...
}
CodecInfo* pInfo = new CodecInfo;
GetAudioCodecPtr()->GetCodecInfo(pInfo); // Call AudioCodec::GetCodecInfo to get codec information
AudioCodec::GetCodecInfo( CodecInfo* pInfo) // The parameter here should not use a single pointer
{
memcpy(pInfo, m_codecInfo, sizeof(CodecInfo));
}
The parameter of the<span><span>AudioCodec::GetCodecInfo</span></span> interface should not be a single pointer; it should use a double pointer. The modified code should be as follows:
AudioCodec::GetCodecInfo( CodecInfo** pInfo) // The parameter type here uses a double pointer
{
memcpy(*pInfo, m_codecInfo, sizeof(CodecInfo));
}
9. Forgetting to include the C runtime library and MFC library when releasing the exe program
For example, a beginner uses the VS-MFC library to write a testing tool software, but when releasing the program in release mode, they forget to include the C runtime library that the program depends on, resulting in the tool software failing to start on some computers with an error indicating that the C runtime library cannot be found:
This is because the program depends on the dynamic version of the runtime library and the MFC library. When releasing the program, these libraries must be included. Some systems may not have these libraries, and the program will fail to start due to missing libraries.
10. Using shallow copy instead of deep copy
What should have been a deep copy was mistakenly done as a shallow copy (direct assignment), causing two different lifecycle C++ objects to point to the same memory. When one object releases the memory, the other object attempts to access that memory, leading to a memory access violation and an exception.
There is a classic C++ interview question that asks us to implement the related functions of the String class, primarily to assess the understanding of deep copy versus shallow copy. The question provides the declaration of the String class:
class String{
public:
String();
String(const String & str);
String(const char* str);
String& operator=(String str);
char* c_str() const;
~String();
int size() const;
private:
char* data;
};
We are asked to implement the internal workings of the above functions. The implementation code for these functions is as follows:
// Regular constructor
String::String(const char *str)
{
if (str == NULL)
{
m_data = new char[1];// Points: Automatically allocate memory for the null-terminator of an empty string, bonus points: NULL check for m_data
*m_data = '\0';
}
else
{
int length = strlen(str);
m_data = new char[length + 1];// Better if NULL check is added
strcpy(m_data, str);
}
}
// Destructor for String
String::~String(void)
{
delete[] m_data; // or delete m_data;
}
// Copy constructor
String::String(const String &other)// Points: Input parameter is const
{
int length = strlen(other.m_data);
m_data = new char[length + 1];// Better if NULL check is added
strcpy(m_data, other.m_data);
}
// Assignment operator
String & String::operator = (const String &other) // Points: Input parameter is const
{
if (this == &other)// Points: Check for self-assignment
return *this;
if (m_data)
delete[] m_data;// Points: Release original memory resources
int length = strlen(other.m_data);
m_data = new char[length + 1];// Bonus points: NULL check for m_data
strcpy(m_data, other.m_data);
return *this;// Points: Return reference to this object
}
Simply sharing the joy of learning; please forgive any errors!


Some screenshots of electronic books

【Complete Set of Hardware Learning Materials Collection】
