to : Understanding C + + What functions are written silently and called
1: An empty class, if you do not declare yourself, the compiler will declare it (compiler Version) a copy constructor, a copy assignment operator, and a destructor. In addition, if you do not declare any constructors, the compiler will also declare a default constructor for you. All of these functions are public and inline.
2: Only when these functions are called will they be created by the compiler.
3: The destructor created by the compiler is a non-virtual, unless the class's base class itself declares a virtual destructor.
4: The compiler refuses to produce operator= for class in the following cases:
A, the class has a reference member, or a Const constant member:
template<class t>class namedobject {public: namedobject (std:: stringconst t& value); ... Private : std::string& namevalue; // This is a reference Const T ObjectValue; // This is const};
References and constants must be initialized at the time of definition, and assignment operations are not supported. The compiler therefore refuses to create a operator= function for such a class;
b, if a base class declares the copy assignment operator as private, the compiler also refuses to generate a copy assignment operator for its derived class.
. : If you do not want to use a function generated automatically by the compiler, you should explicitly deny
1: If you do not want class to support copy initialization or assignment operations, because the copy constructor and the copy assignment operator are not defined, the compiler will automatically create one, so you do not define the two functions to achieve this purpose.
2: Because the compiler creates functions that are public, you can declare the copy constructor or the copy assignment operator as private in order to prevent these functions from being created. This prevents the compiler from creating these functions, and the users of the class cannot invoke them.
3: The above approach is still a loophole, because the class's member functions and friend functions can still call your private function. In this case, you can simply declare and not define them. In this case, if there are member functions or friend functions calling them, a connection error will occur.
Therefore, the copy constructor and assignment operators are declared private and do not define them, and when the user of the class attempts to copy, the compiler blocks him; If you do so in a member function or a friend function, the connector will complain.
4: It is possible to move a connection-period error to the compile time (and then, after all, the sooner the error is detected, the better it is), just define a uncopyable class and inherit your class from that class:
class uncopyable { protected : // allow construction uncopyable () {} // and destruction of ~uncopyable () {} // derived objects ... private : uncopyable ( const uncopyable&); // ... but prevent copying uncopyable& operator = (const Uncopyable&
When anyone (including member functions or friend functions) attempts to copy a derived class object of the Uncopyable class, the compilation period tries to generate a copy constructor and a copy assignment operator. The compiler version of these functions attempts to invoke the corresponding function of its base class, and those calls are rejected by the compiler because the copy function of its base class is private.
- : Declares virtual for a polymorphic base class Destructors
1: When the derived class object is deleted by a base class pointer, and the base class carries a non-virtual destructor, the result is undefined. The actual execution usually occurs when the object's derived component is not destroyed, and its base class component is usually destroyed, resulting in a bizarre "partial destroy" object.
2: Any class with the virtual function is almost certain that there should be a virtual destructor as well.
3: If class does not contain a virtual function, it usually means that it does not want to be used as a base class. When class does not attempt to be treated as a base class, it is often a steamed bread idea to make its destructor virtual.
To implement the virtual function, an object must carry certain information, which is used primarily to determine which virtual function should be called at run time. This information is usually pointed out by a so-called vptr (virtual table pointer) pointer. Vptr points to an array of function pointers, called VTBL (virtual table), and each class with the virtual function has a corresponding VTBL. When an object calls a virtual function, the actual function that is called depends on the VTB that the object vptr refers to----where the compiler looks for the appropriate function pointer.
Therefore, the arbitrary declaration of a class's destructor to virtual will increase the object's volume (vptr). Therefore, many people's experience is: only if the class contains at least one virtual function, it declares the virtual destructor.
, : Don't let exceptions escape destructors
C + + does not prohibit the destructor from spitting out exceptions, but it does not encourage this. If a class's destructor is likely to throw an exception, either: Call Abort to exit the program when an exception is thrown, or swallow the exception when throwing an exception, logging only.
the : Never call virtual during construction and destruction function
1: Do not re-construct functions and destructors, call the virtual function.
2: During the base class construct, if the virtual function is called in the constructor, the virtual function is also the version in base class, even if the derived class is currently being constructed (the base section of the derived class object needs to be constructed first). In other words, the virtual function is not a virtual function during the base class construction.
During the base class construction of the derived class object, the object's type is base class rather than derived class. Not only the virtual functions are parsed by the compiler to the (resolve to) base class, and if the runtime type information is used (runtime types information, such as dynamic_cast and typeID), the object is also treated as a base Class type.
3: The same principle applies to destructors as well. Once the derived class destructor starts executing, the derived class member variables within the object render undefined values, so C + + sees them as if they no longer exist. After entering the base class destructor, the object becomes a base class object.
4: Make sure that none of your constructors and destructors call the virtual function, and that all of the functions they invoke are subject to this constraint.
Ten : Make operator= returns a reference to *this
1: When assigning a value, it can be written in a chain form: x = y = z = 15; the assignment takes a right-associative law, so this expression is equivalent to: x = (y = (z = 15));
2: In order to implement "chained assignment", the assignment operator must return a reference to the left argument of the operator. This is the protocol you should follow when implementing the assignment operator for classes:
operator= (const widget& RHS) // return type is a referenceto { //< /c6> the current class ... return *this; // return the left-hand object}
3: This protocol applies not only to standard assignment forms, but also to all assignment-related operations, such as + =.
One : In operator= handling "self-assignment" in
1: "Self-assignment" occurs when an object is assigned to itself, do not assume that the client will never do so, and self-assignment is not always so can be recognized, such as: a[i] = a[j]; In this statement, if I and J are the same, this is self-assignment; again: *px = *py If PX and py happen to point the same, this is self-assignment.
2: "Self-assignment" may fall into the trap of "accidentally releasing it before stopping the use of the resource." Like what:
widget& Widget::operator= (const widget& RHS) { delete pb; New Bitmap (*RHS.PB); return *this; }
The self-assignment problem here is that the *this and RHS within the operator= function may be the same object. If so, delete is not just destroying the current object's bitmap, it also destroys the RHS bitmap.
To prevent such errors, the traditional practice is to achieve the "self-assignment" test by operator= the first "identity test":
widget& Widget::operator= (const widget& RHS) { if ( this return *this; // identity Test:if A self-assignment, // Do nothing Delete PB; New Bitmap (*RHS.PB); return *this;}
3: The new version of the book still has an unusual problem. More specifically, if "new Bitmap" causes an exception (either because of insufficient memory at the time of allocation or because of an exception thrown by the Bitmap copy constructor), the widget will eventually hold a pointer to a deleted Bitmap.
Happily, giving operator= "exceptional security" tends to automatically get "self-assigning security" in return. Therefore, more and more people's attitude toward "self-assignment" is inclined to leave it alone, focusing on the realization of "abnormal security". For example, we just need to be careful not to remove PB until we replicate what PB refers to:
widget& Widget::operator= (const widget& RHS) { *porig = PB; // Remember original PB New Bitmap (*RHS.PB); // Make PB point to a copy of *PB delete Porig; // Delete the original PB return *this;}
Now, if "new Bitmap" throws an exception, PB remains intact. Even without a test, this code can handle self-assignment because we have made a copy of the original bitmap, deleted the original bitmap, and then pointed to the new manufacturing copy. It may not be the most efficient way to deal with "self-assignment", but it can work.
4: An alternative to ensuring that code is not only "exceptionally secure" and "self-assigning security" within the operator= function is to use the so-called copy and swap technique.
It is a common and well-operator= way to write:
widget& Widget::operator= (const widget& RHS) { Widget temp (RHS); // Make a copy of RHS ' s data Swap (temp); // swap *this ' s data with the copy ' s return *this;}
Or, perhaps more commonly, the following is the notation:
widget& Widget::operator=(Widget rhs) { swap (RHS); return *this;}
A : Don't forget every ingredient when copying an object
1: If you write the copy constructor or assignment operator instead of the version of the compiler, be aware that if you add a member variable for class, you must also modify the copy constructor and assignment operator functions (you also need to modify all constructors of class, and any non-standard form of operator= (e.g. + =). If you forget, the compiler is unlikely to remind you.
2: Whenever you assume the responsibility of "writing copying function for derived class", you must carefully copy its base class component. Those components are often private, so you should let derived class's copying function call the corresponding base class function.
Effective C + +: 02 constructs, destructors, assignment operations