Constructors, Destructors, and Assignment Operators Literature: Effective C++, Third Edition Chapter 2
Overview Autogenerated Functions in Classes Prevent Autogeneration Explicitly Declare Destructors Virtual in Base Classes Don't call virtual functions during construction or destruction Handle assignment to self in operator= Copy all elements, when you are copying
Autogenerated Functions in Classes class Empty{ }; These two codes are THE SAME! - Default constructor - Copy constructor - Destructor - Copy assignment operator are created are autogenerated, if necessary and possible. Exceptions are discussed on the next slides. class Empty{ public: Empty ( ) { } // default constructor Empty(const Empty& rhs) { // copy constructor } ~Empty(){ } // destructor Empty& operator=(const Empty& rhs) { } // copy assignement // operator }; Empty empty1; Empty empty2 (empty1); empty2 = empty1;
- Autocreated constructor/destructor do nothing special (only usual creation of member variables etc.) - Destructor is virtual, if class inherits from base class with virtual destructor - Copy constructor, assignment operator initialize each non-static member variable from left side with value of right side.
No Autogeneration - constructor if there there is any constructor defined. This means as well, you can force a parameter is given. - Copy Assignment Operator - if the object has references or constants as member variables because C++ doesn't allow change of references or constants expect at initialization. - if class has base class with Copy Assignment Operator private, because class attempts to call operator of base class.
Prevent Autogeneration Explicitly Two ways to do so: Way 1 - declare your own versions of the functions privately only friend and member functions have access; write no definition linker error when used. Way 2 - get error at compile time, when using a base class with private function. You may use private inheritance e.g. class QuantumParticle{ public: Private: QantumParticle(const QuantumParticle&); // NOTE: DECLARATION ONLY; // NO DEFINITION QuantumParticle& operator=(const QuantumParticle&); }; Class QuantumParticle: private noncopyable A base class noncopyable can be written by yourself, or taken from the boost package.
Declare Destructors Virtual in Base Classes Virtual destructors do two things - makes sure, that a pointer to the base type calls the destructor of potential daughter classes. - makes the object bigger. Background info: - if you have a virtual function, an object of this type will have an additional internal pointer, that defines, which function is actually called. This pointer takes space, but makes sure, that you can overwrite the function by making the pointer pointing to a derived object function. ObjectBase virtual function [extra pointer] XYZObject function
Problems occur in this case: Note: STL containers are NOT supposed to be base classes. class SpecialString : public std::string { } SpecialString* specialstring = new Special String( Impending Doom ); std::string* string; string = specialstring; // autocast to string delete string; // string doesn't now of derived class, delete // just deletes the string part of the object // UNDEFINED BEHAVIOUR
Don't call virtual functions during construction or destruction Background info: - if you construct a derived class, the constructor of the base class is called BEFORE the constructor of the derived class - the object is in the beginning of type of the base class, before the derived class constructor is called. Therefore the call to the virtual function will act as defined in the base class, not the derived class, even when the initial call is from the derived class. This is rarely what you want (maybe sometimes it is). class BaseClass{ public: BaseClass{function();} virtual function(); } class DerivedClass{ public: DerivedClass{} function(); } NOT used in constructor, but for later calls.
- if you need information from derived class in base class construction, try using a base class constructor and call this constructor in the base class initialization list. In the initialization list, you can specify which constructor to use from the base class, but this has to be the first entry of the list. By this, you can as well make use of base classes without default constructor. B() :A(3,4), b(5) {} //possible constructor class A { public: A(int m,int n) :m(m), n(n) {} private: int m; int n; }; class B : public A { public: B() :b(5) // error : default // constructor to initialize // A is not found {} private: int b; };
Handle assignment to self in In loops things like operator= Object1 = Object1 can happen easily, e.g. if you do a[i] = a[j]; and i = j once in the loop. Resource Managing Objects, e.g. with pointers, that are responsible to delete, what they are pointing to, you can get in trouble, when first deleting and then assigning the deleting object. Therefore do things like this or test for self assignment explicitly: Object* objectorig = pointer; pointer = new Object (*rhs.pointer) // rhs being the object copied from delete objectorig; // delete the old return *this;
Copy all elements, when you are copying When writing your own copy constructor and assignment operators, be careful to - copy all data members you want to have copied, so be careful when add data members later - call the base class partners in derived classes, e.g. : BaseClass(rhs) in the initialization list BaseClass::operator=(rhs); in the body of the assignment operator. - If you need both, a copy constructor and an assignment operator, NEVER call one from the other for easier implementation. Use a 3. function!
Summary Keep in mind, which functions are created automatically and disallow them explicitly, if they don't do what you want. Make destructors virtual in base classes, if derived objects may ever be casted to the base class. Avoid virtual destructors otherwise. [Exceptions must not leave Destructors.] Don't call virtual functions during construction or destruction. Usually it doesn't what you want in derived objects. [Have assignment operators return a reference to *this.] Make sure self assignment is safe (operator=), make sure functions work, which operate on two objects of same type, if they get the same object twice. Be careful with copy constructors and assignment operators when writing them, especially in derived classes.