Monday 2 April 2012

Special class methods for object creation, assignment and destruction

Object in C++ can be created as a:
  • global (on the stack)
  • local (on the stack)
  • dynamic (on the heap)
  • temporary object by explicitly calling its constructor (e.g. passing temporary object to function)
  • temporary object when created implicitly by compiler (e.g. when passing object to function by value)
  • data member of another class (created when another class is created)
  • base class sub-object of the instance of the inherited class (base part)

There are three types of C++ class constructor:
  • default constructor - the one that can be called with no parameters (it does not have any parameters or all parameters have default values); can be implicitly (compiler-) or user-defined; if it is not user-defined and class does not contain const or reference members, it will be created by compiler and will be public
  • non-default constructor - must be called with at least one argument; 
  • copy constructor - called when one object is created from another (already created);  has a single parameter of type A&, const A&, volatile A&, or const volatile A&; can be implicitly (compiler-) or user-defined; if it is not user-defined, it will be created by compiler and will be public; implicit copy-constructor performs shallow copy (memberwise copy); user-defined copy constructor can have additional parameters but they all must have default values

Assignment operator (operator=) assigns value of one (already created) object to another (already created object). If the left-hand side object is being created from the already existing right-hand side object then a copy-constructor will be called (not assignment operator!). operator= can be implicitly (compiler-) or user-defined; if it is not user-defined, it will be created by compiler and will be public; implicit assignment operator performs shallow copy (memberwise copy).

Destructor is another special function that is generated by compiler if it is not provided by the user. When objects created on the stack go out of the scope, their destructor will be called automatically. Destructor must be explicitly called for dynamic objects otherwise we would have memory leaks.

The following example demonstrates relying on compiler-generated copy-constructor and assignment operator:



Output:
goo(): n = 1
c0:
C::C(): n_ = 0
c1:
C::C(): n_ = 1
c2:
c3:
c4:
C::C(): n_ = 0
c5:
C::C(): n_ = 5
C::~C(): n_ = 5
C::~C(): n_ = 1
C::~C(): n_ = 1
C::~C(): n_ = 1
C::~C(): n_ = 1
C::~C(): n_ = 0

The following example demonstrates program output when using user-defined copy-constructor and assignment operator:



Output:
goo(): n = 1
c0:
C::C(): n_ = 0
c1:
C::C(): n_ = 1
c2:
C::C(const C&): rhs.n_ = 1
c3:
C::C(const C&): rhs.n_ = 1
c4:
C::C(): n_ = 0
C::operator=(const C&): rhs.n_ = 1
c5:
C::C(): n_ = 5
C::~C(): n_ = 5
C::~C(): n_ = 1
C::~C(): n_ = 1
C::~C(): n_ = 1
C::~C(): n_ = 1
C::~C(): n_ = 0

One interesting thing happened in line (4) - we would expect copy-constructor to be called here but normal constructor is called instead. This is the consequence of compiler's optimization called Copy elision. When temporary object is meant to be created only to initialize object that is to be constructed, compiler only constructs target object, passing arguments of temporary object constructor to it.

If we want to make our class non-copiable and non-assignable we just have to declare copy-constructor and assignment operator as private, without providing definitions! Our declaration tells compiler not to generate these methods so there will be no implicit definitions. If we call them through public access - the code won't compile. If we try to call them inside class method, the code will compile but linker will complain as it could not find definitions for these two methods.



Compiler output:
(1) error C2248: 'C::C' : cannot access private member declared in class 'C'
(2) error C2248: 'C::C' : cannot access private member declared in class 'C'
(3) error C2248: 'C::C' : cannot access private member declared in class 'C'
(4) error C2248: 'C::C' : cannot access private member declared in class 'C'

Links and references:

Constructors (C++)
Constructor (object-oriented programming) (Wikipedia)
Copy constructor (Wikipedia)
Assignment operator (C++) (Wikipedia)
The Anatomy of the Assignment Operator by Richard Gillam
Destructor (Wikipedia)
Constructors (by Danny Kalev)
Why is the copy constructor not called? (SO)

No comments: