[content]
Items 5: know what functions C++ silently writes and calls
WHY: If there are user-defined constructors / destructors, the compiler will not generate default constructors / destructors.
WHY: If there is a const or reference data member, the compiler will not generate default copy constructors. Because const value and what reference refers to, can not be modified.
WHY: If base class has private copy constructor, compiler will not generate default copy constructors for derived class. Because the derived class has no access to the base class’s private member.
WHY: Compiler will generate default move constructor, if the followings are true
1. No user-defined move constructor or move assignment constructor.
2. No user-defined copy constructor or copy assignment constructor.
3. No user-defined destructor.
Items 6: Explicitly disallow compiler-generated functions you do not want
DO: Different ways to disallow default functions
1. Declare the function as a private member and do not define them. There will be link-time errors if they are called. (Because there is no definition.)
2. Inherit a base class, where the function is declared private. There will be a compile-time error if they are called. (Because base class’s private function cannot be called from derived class.)
3. Use “deleted” keyword.
Items 7: Declare destructor virtual in polymorphic base class
DO: Declare virtual destructors for polymorphic base class. Because each derived class should have its own destructor implementation. Don’t declare virtual destructors if the class is not used polymorphically.
Items 8: Prevent exceptions from leaving destructors
DO: There are different ways to handle exceptions in destructors
1. Terminate the program: reasonable option to avoid undefined behavior
2. Move the exception code outside destructor and give the caller a chance to handle it
3. Swallow the exception: generally a bad idea, but you need to make sure the program can work properly after the fault
WHY: Avoid exceptions in destructors, because it could lead to memory leak and undefined behavior.
Items 9: Never call virtual functions during construction and deconstruction
DO: If we need to call customized functions in constructor and destructor, we can avoid calling a virtual function directly, but pass in the various information to the base class constructor, so that the base class runs the customized logic for different derived classes.
class Transaction {
public:
Transaction() { LogTransaction(); } // When the derived class calls base class
// constructor, polymorphism is not working.
virtual void LogTransaction() const = 0;
}
WHY: When a base class constructor is called, it is considered as a base class, and it will not invoke derived class functions. If we call a virtual function in base constructor, it will only call the definition in the base class, it will not call its derived class’s implementation.
Items 10: Have assignment operators returns a reference to *this
DO: Return *this for assignment operators, so that you can chain assignment operations and they are right associative.
class Widget {
public:
Widget& operator=(const Widget& rhs) {
return *this;
}
}
Items 11: Handle assignment to self in operator=
DO: In general, code that operates on references or pointers to multiple objects of the same type, need to consider objects might be the same.
Widget& Widget::operator=(const Widget& rhs) {
delete pb;
pb = new Bitmap(*rhs.pb); // pb could be already deleted.
return *this;
}
DO: Different approaches to solve self-assignment issues.
1. Test for self-assignment and handle it separately. This will cause inefficiency because it is usually rare to have self-assignment and the test is wasteful.
2. Copy and Swap approach.
Items 12: Copy all parts of an object
DO: when implementing a copy function, remember to also copy the data member from base class. You can invoke the appropriate copy function from base class.
DO: when adding a new data member, remember to update the copy function too.
DO: avoid calling copy function from copy assignment function and vice versa. If you need to do so, consider moving the shared part to a function called init.