01
Classes containing member variables must define a constructor or a default constructor.
Note: If a class has member variables and does not define a constructor or a default constructor, the compiler will automatically generate a constructor, but the compiler-generated constructor will not initialize the member variables, leaving the object in an uncertain state.Exception: If this class inherits from another class and does not add member variables, a default constructor does not need to be provided.Example: The following code does not have a constructor, and the private data members cannot be initialized:
class CMessage { public: void ProcessOutMsg() { //… } private: unsigned int msgid; unsigned int msglen; unsigned char *msgbuffer; }; CMessage msg; //msg member variables are not initialized msg.ProcessOutMsg(); //Subsequent use poses risks//Therefore, it is necessary to define a default constructor, as follows:class CMessage { public: CMessage (): msgid(0), msglen (0), msgbuffer (NULL) { } //... };
02
To avoid implicit conversions, declare single-parameter constructors as explicit.
Note: If a single-parameter constructor is not declared as explicit, it will become an implicit conversion function.
Example:
{ public: explicit Foo(const string &name):m_name(name) { } private: string m_name; }; ProcessFoo("zhangsan"); //Compiler error during function call, as implicit conversion is explicitly prohibited
Defined Foo::Foo(string &name), when the parameter is a Foo object and the argument is a string, the constructor Foo::Foo(string &name) is called and converts the string into a temporary Foo object passed to the calling function, which may lead to unintended implicit conversions.The solution: Add explicit before the constructor to restrict implicit conversions.03
Classes that manage resources should define custom copy constructors, assignment operators, and destructors.
Note: If the user does not define them, the compiler will generate default copy constructors, assignment operators, and destructors.The automatically generated copy constructor and assignment operator simply perform shallow copies of all source object members to the destination object; the automatically generated destructor is empty. This is insufficient for classes that manage resources: for example, resources allocated from the heap, shallow copies will cause the source and destination object members to point to the same memory, leading to double freeing of resources. An empty destructor will not free allocated memory.If copy constructors and assignment operators are not needed, they can be declared as private to disable them.Example: If a structure or object contains pointers, define your own copy constructor and assignment operator to avoid dangling pointers.
class GIDArr { public: GIDArr() { iNum = 0; pGid = NULL; } ~GIDArr() { if (pGid) { delete [] pGid; } } private: int iNum; char *pGid; GIDArr(const GIDArr& rhs); GIDArr& operator = (const GIDArr& rhs); };
04
Make operator= return a reference to *this.
Note: This conforms to the common usage and habit of chained assignment.
String& String::operator=(const String& rhs) { //... return *this; //Return the left object}
05
Check for self-assignment in operator=.
Note: Self-assignment and normal assignment have many differences, and if not guarded, problems may arise.
Example:
class String { public: String(const char *value); ~String(); String& operator=(const String& rhs); private: char *data; }; //Self-assignment, validString a; a=a; //Bad example: Ignoring self-assignment leads to accessing dangling pointersString& String::operator=(const String& rhs) { delete [] data; //Delete data //Allocate new memory and copy rhs's value to itdata = new char[strlen(rhs.data) + 1]; //rhs.data has been deleted, becoming a dangling pointerstrcpy(data, rhs.data); return *this; }//Good example: Check for self-assignmentString& String::operator=(const String& rhs) { if(this != &rhs) { delete [] data; data = new char[strlen(rhs.data) + 1]; strcpy(data, rhs.data); } return *this; }
Assign all data members in the copy constructor and assignment operator.
Note: Ensure the integrity of the objects in the constructor and assignment operator, avoiding incomplete initialization.
06
When performing delete operations through base class pointers, the base class destructor should be public and virtual.
Note: Only if the base class destructor is virtual can the derived class destructor be called.Example: Memory leak caused by the absence of a virtual destructor in the base class definition.
//The following platform defines base class A, which implements the functionality of obtaining version numbers.class A { public: virtual std::string getVersion()=0; }; //Product derived class B, implementing its specific functionality, defined as follows:class B:public A { public: B() { cout<<"B()"<<endl; m_int = new int [100]; } ~B() { cout<<"~B()"<<endl; delete [] m_int; } std::string getVersion(){ return std::string("hello!");} private: int *m_int; }; //Simulated interface calling code as follows:int main(int argc, char* args[]) { A *p = new B(); delete p; return 0; }
The derived class B, although it performs resource cleanup in its destructor, unfortunately, the derived class destructor will never be called. Since the base class A does not define a destructor, let alone a virtual destructor, when the object is destroyed, only the system’s default destructor will be called, leading to memory leaks.07 Avoid calling virtual functions in constructors and destructors.Note: Calling virtual functions in constructors and destructors can lead to undefined behavior. In C++, a base class constructs a complete object only once.Example: Class BaseA is the base class, DeriveB is the derived class.
class BaseA //Base class BaseA { public: BaseA(); virtual void log() const=0; //Different derived classes call different log files}; BaseA::BaseA() //Base class constructor{ log(); //Call virtual function log }class DeriveB:public BaseA //Derived class{ public: virtual void log() const; };
When executing the following statement:DeriveB B; will first execute the constructor of DeriveB, but will first call the constructor of BaseA, and since the constructor of BaseA calls the virtual function log, at this point log is still the base class version, only after the base class is fully constructed will the derived class construction be completed, leading to undefined behavior. The same reasoning applies to destructors.08
Define the parameters of the copy constructor and assignment operator as const reference types.
Note: The copy constructor and assignment operator should not change the object they reference.
Follow me
Click the card below to follow me
↓↓↓

If you find it interesting, please click
Save
Like
View
+1
❤❤❤