1.2.3.4 Basic routines
  • Big five rule: If you need customized copy ctor, assignment, and destructor, Then you also need move ctor and move assignment. Because it include memory allocation inside your class, you need you own copy ctor to perform deep copy and use move ctor to "move resource".
  • Normally, ctor , destructor and assignment should be public. In inheritance context, all the base class desctructor should be virtual.
  • Avoid calling virtual functions in ctor and dtor. "C++ Coding Standards" item 49.
  • What does a typical user defined move constructor do?
    class x : public Base{ 
    Member m_; 
    X(X&& x): Base(std::move(x)), m_(std::move(x.m_)){ 
    x.set_to_resourceless_state(); 
    } 
    }
  • What does a defaulted move assignment do?
    class x : public Base{ 
    Member m_; 
    X& operator=(X&& x) { 
    Base::operator=(static_cast<Base&&>(x)); 
    m_ = static_cast<Member&&>(x.m_); 
    return *this; 
    } 
    }
  • Assuming the only non-static data in the class is a std::string, here’s the conventional way (i.e., using std::move) to implement the move constructor:
    class Widget { 
    Widget(Widget&& rhs) 
    : s(std::move(rhs.s)) 
    { ++moveCtorCalls; } 
    private: 
    static std::size_t moveCtorCalls; 
    std::string s; 
    };
  • If you define a specify ctor, you also need to define default ctor. Because system will not produce any default ctor for you. So below statement will produce error when compiling.
    class obj; //error 
    class* obj = new class(); //error 
    class arra[10] //error 
    template<class T> 
    class Array{ 
    T t; 
    }; 
     
    Array<class> a; //error
  • If no special demand, you can declare you own default ctor and use system implicit generated one. you can use keyword default
    class Empty{ 
     Empty() = default; 
     // you dont need to give implementation of default ctor. 
     Empty(int i) ; 
    }
  • Make Constructors Protected to prohibit direct Instantiation. Make constructors Private to prohibit Derivation.
  • Use default arguments to reduce the number of ctor.
    class Brush{ 
    Brush(); 
    Brush(Color c); 
    Brush(Texture t); 
     
    Brush(Color c= Black, Texture t = Solid); // it will be better. 
    }
  • From previous example, you can see that default ctor is very important. but when class MUST need another information when create, such as worker class, You must provide SSN when you create a worker. At this time, if you create default ctor, It’s not good idea. A NULL SSN will cause a lot of trouble in the future. So you have to use Worker pointer, and vector<Worker>. You also need to use delete to disable default ctor. That is C++ spirit, You never have the best answer, only have context answer.
    class Worker{ 
    char* SSN; 
    Worker(const char*); 
    Worker(){SSN=nullptr); //bad smell. 
    }
  • Normally you will have to explicitly declare your own destructor if:
    1. You are declaring a class which is supposed to serve as a base for inheritance involving polymorphism, if you do you’ll need a virtual destructor to make sure that the destructor of a Derived class is called upon destroying it through a pointer/reference to Base.
    2. You need to release resourced aquired by the class during its leftime Example 1: The class has a handle to a file, this needs to be closed when the object destructs; the destructor is the perfect location. Exempel 2: The class owns an object with dynamic-storage duration, since the lifetime of the object can potentially live on long after the class instance has been destroyed you’ll need to explicitly destroy it in the destructor.
  • A copy constructor is called whenever a new variable is created from an object. This happens:

    1. When a new object is initialized to an object of the same class.
    2. When an object is passed to a functon by value.
    3. When a function returns an object by value.
    4. When the compiler generates a temporary object.
    1) Person r(p);   or  Person p = q;      // copy constructor 
    p = q;  // but not called in assignment; p has existed before 
     
    2) A value parameter is initialized from its argument. 
    fun(Person r)    fun(p); 
    // copy constructor, produce a new object r. 
    //just like Person r = p; 
     
    3) An object is returned by a function. 
    Person fun(); 
    Person r = fun(); 
    //here copy ctor is called twice. 
    // use -fno-elide-constructors in g++ produce two copy ctor 
    // or It will use Return value optimization. 
     
    4) class(string &a, string &b): m_a(a),m_b(b){} 
    //Initialization list
  • In previous example, you can see when you pass value of obj, It will call copy ctor, It’s not very efficient. So you should use reference or pointer if it’s possible, don’t pass object directly.
  • An Assignment operator examples: 1) avoid assignment self 2) return *this reference.
    class & class::operator=(class &a){ 
      if(this == &a) 
        return *this;  //avoid assignment 
        .............  // assignment operation here. 
        return *this ; // return *this reference. 
    }
  • For reference, once assigned, a reference cannot be re-assigned. So if a class has a reference member, It can be initialized by initializer list in ctor and copy ctor. But you can’t overload assignment operator any more, If you really need assignment operator, change reference to pointer