[C++][Chap. 2] Constructor, Destructor and Assignment Operators

[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.

Leave a Reply

Your email address will not be published. Required fields are marked *