Constructors and destructors are fundamental to the concept of classes in C++. Both constructor and destructor are more or less like normal functions (but with some differences) that are provided to enhance the capabilities of a class.
Constructor, as the name suggests is used to allocate memory (if required) and construct the objects of a class while destructor is used to do the required clean-up when a class object is destroyed. In this article, we will study the concept of constructors and destructors through working examples.
If you are new to C++, you should first understand the concept of C++ classes.
Constructors and Destructors
Let’s understand the concept of constructors and destructors through this example :
#include<iostream> class country { public: country() { std::cout<<"\n Constructor called \n"; } void setNumOfCities(int num); int getNumOfCities(void); ~country() { std::cout<<"\n Destructor called \n"; } private: int num_of_cities; }; void country::setNumOfCities(int num) { num_of_cities = num; } int country::getNumOfCities(void) { return num_of_cities; } int main(void) { country obj; int num = 5; obj.setNumOfCities(num); num = obj.getNumOfCities(); std::cout<<"\n Number of cities is equal to "<<num; return 0; }
In the example above :
- The name of the class is country.
- Observe that there are two functions with same name as that of class ie country.
- The function that have a ‘~’ before the name is destructor while the other one is constructor.
As we have already discussed that a constructor is used for creating an object. In precise terms, a constructor is a special function that gets called automatically when the object of a class is created. Similarly, a destructor is a special function that gets called automatically when a class object is deleted or goes out of scope.
Let’s verify the above given explanation by executing the code shown above.
Here is how a C++ code is compiled on Linux :
$ g++ -Wall cnstrDestr.cpp -o cnstrDestr $
So we see that we used g++ compiler for compiling the c++ code and in our case, the code compiled without any warning or error. Now let’s execute the code :
$ ./cnstrDestr Constructor called Number of cities is equal to 5 Destructor called
Observe that as the object of the class was created, constructor of the class was executed and just when the object was about get destroyed, the destructor was called. So this confirms that these special functions are called internally or automatically (from a developer’s point of view).
Now one would ask what’s the actual role of these functions? In which scenarios are they really required?
Well, to answer this question, lets assume that in the same program (shown above), the call to setNumOfCities() and getNumOfCities() is interchanged. This means, now the code tries to get the value before setting it.
Here is the updated code :
#include<iostream> class country { public: country() { std::cout<<"\n Constructor called \n"; } void setNumOfCities(int num); int getNumOfCities(void); ~country() { std::cout<<"\n Destructor called \n"; } private: int num_of_cities; }; void country::setNumOfCities(int num) { num_of_cities = num; } int country::getNumOfCities(void) { return num_of_cities; } int main(void) { country obj; int num = 5; num = obj.getNumOfCities(); obj.setNumOfCities(num); std::cout<<"\n Number of cities is equal to "<<num; return 0; }
When this code is executed, here is the output :
$ ./cnstrDestr Constructor called Number of cities is equal to 134514633 Destructor called
Observe that some garbage value is produced in the output. This is because the value of the variable ‘num_of_cities’ was fetched even before some value was assigned to it. Now, what could be a possible solution to this problem? One could think of initializing the variable with some default value in the class itself.
Something like :
#include<iostream> class country { public: country() { std::cout<<"\n Constructor called \n"; } void setNumOfCities(int num); int getNumOfCities(void); ~country() { std::cout<<"\n Destructor called \n"; } private: int num_of_cities = 0; }; void country::setNumOfCities(int num) { num_of_cities = num; } int country::getNumOfCities(void) { return num_of_cities; } int main(void) { country obj; int num = 5; num = obj.getNumOfCities(); obj.setNumOfCities(num); std::cout<<"\n Number of cities is equal to "<<num; return 0; }
Well, can we do this? Let’s compile this code and verify :
$ g++ -Wall cnstrDestr.cpp -o cnstrDestr cnstrDestr.cpp:23:25: error: ISO C++ forbids initialization of member ‘num_of_cities’ [-fpermissive] cnstrDestr.cpp:23:25: error: making ‘num_of_cities’ static [-fpermissive] cnstrDestr.cpp:23:25: error: ISO C++ forbids in-class initialization of non-const static member ‘num_of_cities’
Well, the compiler throws error complaining that this cannot be done as this variable is non-static. So, this is not the correct way of doing things. Then how can the variable be initialized with a default value? Yes, you guessed it correct, through constructors. Since constructor is also a member function of class so they can access class private data members.
Here is how it can be done :
#include<iostream> class country { public: country() { num_of_cities = 0; std::cout<<"\n Constructor called \n"; } void setNumOfCities(int num); int getNumOfCities(void); ~country() { std::cout<<"\n Destructor called \n"; } private: int num_of_cities; }; void country::setNumOfCities(int num) { num_of_cities = num; } int country::getNumOfCities(void) { return num_of_cities; } int main(void) { country obj; int num = 5; num = obj.getNumOfCities(); obj.setNumOfCities(num); std::cout<<"\n Number of cities is equal to "<<num; return 0; }
Now let’s compile and execute the above code:
$ g++ -Wall cnstrDestr.cpp -o cnstrDestr $ ./cnstrDestr Constructor called Number of cities is equal to 0 Destructor called
Observe that compilation was successful and expected output was produced. So, this should give you a good idea about the power of constructors and destructors.
In real world scenarios, constructors are used to initialize the data members of a class and most importantly to allocate memory to pointers and destructors are used for clean-up of this memory.
Here is an example :
#include<iostream> class country { public: country() { num_of_cities = new(int); std::cout<<"\n Constructor called \n"; } void setNumOfCities(int num); int getNumOfCities(void); ~country() { if(num_of_cities) delete num_of_cities; std::cout<<"\n Destructor called \n"; } private: int *num_of_cities; }; void country::setNumOfCities(int num) { *num_of_cities = num; } int country::getNumOfCities(void) { return (*num_of_cities); } int main(void) { country obj; int num = 5; obj.setNumOfCities(num); num = obj.getNumOfCities(); std::cout<<"\n Number of cities is equal to "<<num; return 0; }
Here are some of the important points about constructors and destructors :
- They are called whenever a class object is created and destroyed (or goes out of scope).
- Constructors and destructors are usually kept public in scope.
- Both constructors and destructors have same name as that of class and do not have return types. This means that they cannot return values like any other normal function.
- If constructor or destructor is not provided explicitly, compiler generates one internally.
- A default constructor, if explicitly declared in class, is one that accepts no arguments or the one that have arguments with default values.
- Constructors and destructors cannot be inherited.
- Constructors can be overloaded.
- Destructors cannot accept arguments.
Note that we have not yet covered the concept of inheritance in C++. We will discuss some of the characteristics of constructors and destructors in detail when we will discuss inheritance.
Comments on this entry are closed.
Great article. Thanks!!!
This is great I’m learning C++ and this will teach me a few more things, I like it. Keep it coming.
Thanks for this article, I really appreciate your work.
Just an improvement regarding best practices regarding defensive programming:
Change :
if(num_of_cities)
To :
if(num_of_cities != NULL)
1) It is more appropriate to use explicit coding, for reading and comprehension
2) An explicit test may detect an bug, for instance, if you use the wrong variable which would not be a pointer
3) Unix philosophy states that first priority is building a program that works as designed, and then, measure performance, and if needed use appropriate optimization. In case this gives a better performance, you may write you simplified test – but I doubt this would make a useful difference.
— Philippe
I’ve been very impressed with all of the examples I’ve read. This was one of the best. Very succinct and well presented.
I usually like the posting here but I do have a number of issues with this one. I guess you maybe looking to cover at a later dete so I applogise if that’s the case. My problems are:
(1) Poor style – with constructors it is best practice to always use the “member-initialisation-list” syntax, i.e.
country(): num_of_cities(new(int))
{
…
In this case (a pointer) it’s not essential but there are cases it is needed, so is generally considered good practice (see rule 48 in C++ Coding Standard by Sutter & Alexandrescu)
(2) prefer new int() over new(int) as this will zero initialise the dynamic memory (works for all base types)
(3) You haven’t covered the cobcept of RAII, i.e. using the constructor to initialise the memory, e.g.
country(int num): num_of_cities(new int(num))
{
…
int main()
{
country obj(5);
(4) By using an exmplae with pointers and delete in the destructor you have introduced a potential memory leak, as in:
country obj(5);
country obj1(obj); // copy constructor with shallow copy
country obj2(0);
obj2 = obj; // op= with shallow copy
This code will cause interesting behavior (core dump), so either you must cover overloading the copy consructor and operator= or, better still you should be encoraging use of shared_ptr as introducted in tr1 in 2003
(5) With C++11 (supported in g++) initialisation of object member variables is allowed, e.g.
int num_of_cities = 5;
I hope you’ll cover these at a later date.
First of all, there is the difference between functions and methods, I don’t think about those philosoficall but there is the bih one.
The functions dont have this pointer for starters….
And they are not normal functions, there is the diff, and people shouldn’t be confused with those things!!!
And let me tell this one
many times I don’t even write the constructor or destructor, but in the start people should write them, till they got it.
And the ~should be used in different way, now days dot net has something that is called garbage collection.
That is bad, I know this is the Linux world, but it is good to know all the ways, and then to decide witch one is the best for you.
thnx…the article rely helped me…it good
Usefull