A Developer’s Experiences with Constructors And Destructors In C++

By | 04/03/2013

The most compelling feature of C++ programming language is it being object oriented. An object is a fundamental aspect of an object oriented language like C++. The objects come from classes which is a collection of related type of information. As in C, we have structures as a user defined data-type, which is a collection of data members of different data-types. On Similar lines, C++ introduces the concept of classes, though with a lot of basic differences like the concept of constructors and destructors etc.

Let’s discuss a few differences here :

  • C structures just have data members, whereas C++ classes, apart from data members, can also have member functions.
  • C structure data members are all publicly accessible, however C++ classes have the power to specify access as private, public or even protected.
  • Another vital difference are constructors and destructors. To initialize objects, including dynamic memory allocation, implicitly in C++, programmers define constructors, and similarly, to destroy an object, including freeing the memory, it uses destructors. In this article, the we shall have an in-depth understanding of the C++ constructors and destructors.

[Note that, in the explanation above, we have discussed about C structures… C structures carry a lot of differences with C++ structures.]

In this article, we will discuss in detail the intricacies of constructors and destructors from a developer’s point of view.

 

Constructors and Destructors

 

Constructors

In C++, constructors are the functions which have same name as that of class. They construct the data members of class for objects. Constructor is called whenever object of class is created. Constructor does initialization of objects of class.

C++ constructors have following characteristics (each characteristic is listed in bold and then explained):

 

1. They are called automatically when object of class is created. Class object can be created either on stack or on heap (through new operator).

#include <iostream>
using namespace std;

class test
{
    int a;
    public:
    test(int val)
    {
        a=val;
        cout << "a initialized with=" << a << endl;
    }
};

int main()
{
    test obj(5);
    return 0;
}

Here is the Output:

$ ./a.out 
a initialized with=5

Here, we can see that cout printed line that was given within constructor body.

 

2. They should be declared in public section. If it is kept in private section, then object creation of class would not be done, because constructors are called by default and if they are private functions, then its calling would not be allowed form outside of class from where object is being created.

class test
{
    private:
    int a;
    test(int val)            Error would be given here.
    {
        a=val;
        cout << "a initialized with=" << a << endl;
    }
};

int main()
{
    test obj(5);            Error would be given here.
    return 0;
}

Here is the output when the code (shown above) is compiled :

$ g++ constructor.cpp
constructor.cpp: In function ‘int main()’:
constructor.cpp:9:5: error: ‘test::test(int)’ is private
constructor.cpp:19:15: error: within this context

Here, we can see that error is thrown when constructor is given under private section.

 

3. They do not have return types. Even void return type is not allowed; therefore they cannot return any value.

class test
{
    int a;
    public:
    test(int val);
};

test::test(int val)
{
    a=val;
    cout << "a initialized with=" << a << endl;
}

Here, we can see that constructor is defined outside the class and no return type is given.

 

4. They cannot be inherited, though a derived class can call the base class constructor. Base class constructor is called before Derived class constructor when derived class object is created.

#include <iostream>
using namespace std;
class test
{
    public:
    test()
    {
        cout << "test constructor" << endl;
    }
};
class derivedTest : public test
{
    public:
    derivedTest()
    {
        cout << "derivedTest constructor" << endl;
    }
};

int main()
{
    derivedTest obj;
    return 0;
}

Here is the output when above code is run :

$ ./constructor_3
test constructor
derivedTest constructor

Here, we can see that base class constructor is called first and then derived class constructor even though object of derived class is created.

 

5. They can have default arguments. Constructor can have arguments with default value that gets used to initialize data members of class when no value for data member initialization is given during object creation.

#include <iostream>
using namespace std;
class test
{
    int a, b;
    public:
    test(int val1, int val2=0);
};
test::test(int val1, int val2)
{
    a=val1;
    b=val2;
    cout << "Data member 'a' initialized with=" << a << endl;
    cout << "Data member 'b' initialized with=" << b << endl;
}
int main()
{
    test obj1(4);
    test obj2(5, 10);
    return 0;
}

Here is the output when above code is executed :

$ ./constructor_2
Data member 'a' initialized with=4
Data member 'b' initialized with=0
Data member 'a' initialized with=5
Data member 'b' initialized with=10

Here, we can see that constructor has several parameters. Such kind of constructor is also known as parametrized constructor. We can give default values to parameters of constructor. Like here, second argument is given default value ‘0’ that is being used to initialize data member ‘b’ of class when no value is given during object creation.

 

6. Constructors can be overloaded

#include <iostream>

using namespace std;

class test
{
    int a, b;
    public:
    test();
    test(int val1, int val2);
};

test::test()
{
    a=0;
    b=0;
    cout << "Data member 'a' initialized with=" << a << endl;
    cout << "Data member 'b' initialized with=" << b << endl;
}

test::test(int val1, int val2)
{
    a=val1;
    b=val2;
    cout << "Data member 'a' initialized with=" << a << endl;
    cout << "Data member 'b' initialized with=" << b << endl;
}

int main()
{
    test obj1;
    test obj2(4, 8);
    return 0;
}

Here is the output when the code (shown above) is executed :

$ ./constructor_4
Data member 'a' initialized with=0
Data member 'b' initialized with=0
Data member 'a' initialized with=4
Data member 'b' initialized with=8

Here, we can see that constructor is overloaded. When no value for object initialization is given during object creation, then zero argument constructor is called. When values for object initialization are given during object creation, then parameterized constructor is called.

 

7. Address of constructor cannot be referred.

#include <iostream>
using namespace std;
class test
{
    public:
    void test_func()
    {
        cout << "\n address of constructor=" << (void*)&test::test << endl;
        cout << "address of class member func=" << (void*)&test::test_func <<  endl;
    }
};
int main()
{
    test obj;
    obj.test_func();

    return 0;
}

Here is the output when the code (shown above) is compiled :

$ g++ constructor.cpp -o constructor
constructor.cpp: In member function ‘void test::test_func()’:
constructor.cpp:11:56: error: taking address of constructor ‘((test*)this)->*test::test’

Here, we can see that address of constructor cannot be taken. It is illegal and error is thrown by compiler itself. But address of class member functions can be obtained. If in above code, we comment the line in which address of constructor is referred then below output is obtained.

$ ./constructor
address of class member func=0x804881e

 

8. Object of class containing constructor cannot be used as a member of a union.

#include <iostream>
using namespace std;
class test
{
    public:
    test ()
    {
    }
};

union test_union
{
    test obj;
};

int main()
{
    test_union un_obj;
    return 0;
}

Here is the output when code (shown above) is compiled :

$ g++ constructor.cpp -o constructor
constructor.cpp:15:10: error: member ‘test test_union::obj’ with constructor not allowed in union
constructor.cpp:15:10: note: unrestricted unions only available with -std=c++11 or -std=gnu++11

 

9. Constructor cannot be virtual. Basically virtual functions in C++ allow to clearly distinguish between derived class and base functions when function with same name is defined in both derived and base classes and this kind of function is called from base class pointer. But, if this concept of virtual function is applied in constructor, then it will break property of constructor that base class constructor is called first while derived class object is created.

 

Copy Constructor

Copy constructor are the constructors which creates object by copying values of data members of existing class object into new class object to be created. It does copying one object to another object. Copy constructor takes reference of object of its same class as an argument to itself.

#include <iostream>
using namespace std;
class test
{
    int a;
    public:
    test (int val)
    {
        a = val;
        cout << "Data member 'a' in constructor initialized with value=" << a << endl;
    }

    test (test &obj)
    {
        a = obj.a;
        cout << "Data member 'a' in copy constructor initialized with value=" << a << endl;
    }
};

int main()
{
    test obj1(2);
    test obj2(obj1);
    test obj3 = obj2;
    return 0;
}

Here is the output when code (shown above) is executed :

$ ./constructor
Data member 'a' in constructor initialized with value=2
Data member 'a' in copy constructor initialized with value=2
Data member 'a' in copy constructor initialized with value=2

Here, we can see that obj2 is created from obj1 by copy constructor that is called implicitly. Similarly, obj2 is assigned to obj3 during its creation. It is also done by copy constructor.

Why reference?

A valid question is, why does copy constructors make it mandatory to pass the object to be copied by reference? Well, let us see what happens if we pass the object by value.

class Acls
{
     private:
         int data;
         char name[LEN];
    Acls()
    {
         data = 3;
          strncpy(name, NAME, LEN);
    }

//Copy Constructor - pass by value
    Acls( Acls oa)
   {
    data = oa.data;
    strncpy(name, oa.name, LEN);
 }
}

Now, the copy constructor takes the object by value, and here is how we call it

Acls oa;
Acls copy =oa;// Calling the copy constructor

Here, the first statement creates a normal object of class Acls, by calling its simple constructor. However, in the second statement, it will call the copy constructor, with object ‘oa’ being passes by value. During a pass by value in a general method, internally what happens is another instance of the argument is being created as the control moves to caller. That is, separate memory is allocated but the containing values are same, which are to be used by the method.

Hence, in our case as well, another instance of the class ‘Acls’ would be created keeping the values same. However, creating another object with same value, would again call the copy constructor. Since, again in the second call to copy constructor, the argument object is passed by value, again the same flow repeats and so on. Hence, it will lead to an infinite calling of the copy constructor.

Therefore, the g++ compilers are smart enough to enforce passing objects by reference in copy constructors, and will complain about the same with a compiler error.

Lets experience it through following example:

#include <iostream>
using namespace std;
class test
{
    int a;
    public:
    test (int val)
    {
        a = val;
        cout << "Data member 'a' in constructor initialized with value=" << a << endl;
    }

    test (test obj)
    {
        a = obj.a;
        cout << "Data member 'a' in copy constructor initialized with value=" << a << endl;
    }
};

int main()
{
    test obj1(2);

    cout <<"Calling Copy Constructor - pass by value\n";
    test obj2(obj1);
    return 0;
}

Compiling the above example source:

$ g++ cc.cpp -Wall -o cc
cc.cpp:13:19: error: invalid constructor; you probably meant \u2018test (const test&)\u2019

 

Destructors

In C++, destructors are the functions which have same name as of class name prefixed by tilde (i.e. ~). They destroy the class objects that are created by constructor. Destructor is called whenever object of class is destroyed and it does de-initialization of data members of class objects.

C++ destructors have following characteristics:

  • They are called automatically when object of class is destroyed by going out from block or program is exited.
  • Destructor does not take any value as an argument.
  • They do not have return types.
#include <iostream>
using namespace std;
class test
{
    public:
    test ()
    {
        cout << "constructor called" << endl;
    }
    ~test ()
    {
        cout << "destructor called" << endl;
    }
};

int main()
{
    test obj;

    return 0;
}

Here is the output :

$ ./constructor
constructor called
destructor called

Here, we can see that object of test class is created and constructor is called implicitly. When program is exited from main () function, then destructor is called implicitly.

When data member of class is pointer type and it is initialized by allocating dynamic memory, then that data member is required to be freed in destructor to avoid memory leak issues.

#include <iostream>

using namespace std;

class test
{
    int *ptr;
    public:
    test ()
    {
        ptr = new int(5);
        cout << "constructor called with ptr points to " << *ptr << endl;
    }
    ~test ()
    {
        cout << "destructor called" << endl;
        delete ptr;
    }
};

int main()
{
    test obj;

    return 0;
}

Here is the output when code (shown above) is executed :

$ ./constructor
constructor called with ptr points to 5
destructor called

Here, we can see that constructor allocates dynamic memory using new operator on data member and destructor is freeing allocated memory using delete operator. Destructor does proper cleanup of object by freeing up memory allocated by constructor.

If class object is created by applying new operator on pointer to class object, then destructor is not called implicitly when pointer to object goes out of scope.

#include <iostream>
using namespace std;
class test
{
    int *ptr;
    public:
    test ()
    {
        ptr = new int(5);
        cout << "constructor called with ptr points to " << *ptr << endl;
    }
    ~test ()
    {
        cout << "destructor called" << endl;
        delete ptr;
    }
};

int main()
{
   test *obj = new test;
   return 0;
}

Here is the output when the code (shown above) is executed :

$ ./constructor
constructor called with ptr points to 5

Here, we can see that destructor is not called implicitly. To call destructor, delete operator should have been applied on test class base pointer object i.e. “obj”. Please note that constructor in which data member is initialized by dynamic memory using new operator, such constructors are known as dynamic constructor.

Leave a Reply

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