Chapter 12. Constructors and Destructors
Constructors and destructors are special forms of member functions.
Important
- Constructor
A constructor is used to initialize an object
- Destructor
A destructor is used to finalize an object ( = clean it up)
Constructors
When an object instance is created, all member variables have unitizialized values. To make proper use of an object these member variables should be given some values.
Since you do not want to set all attributes manually, we use "constructors".
Imagine the following class:
This class is very useful to count. It is (purposely) written so that the counter can not be set from the outside. Unfortunately C++ does not allow us to initialize variables with values. So how do we do it?
Answer: Constructors
Default Constructor
We define and implement a so called default constructor. A default constructor:
Is called by default when an object instance is created.
Needs no parameters
To define a default constructor we define a function with
the exact same name as the class
no parameters
no return type (not even void)
In most cases the default constructor will be public. However there are cases where you want it private (more about that later)
Usually consturctors are defined as the first methods. Usually default constructors are the first of the constructors
Example:
class Counter { private: int value; public: Counter(); // ... };
Practice:
Define the following class, add a default constructor.
Implementing a default constructor is again the same as implementing that method that has the same name as your class.
What should you do in a constructor?
All member variables should be initialized
If a default value is given in the model, use that
If not, make up good starting values (0 for numbers, null for pointers).
If there is something else your object needs to do before it starts, this is the place to do it.
Example:
Counter::Counter() { value = 0; }
Practice:
Implement the default constructor for the Daisy class defined earlier.
Calling a default constructor:
A default construcotr is called everytime the object is initialized and we don't do anything special. Examples:
Counter *c; c = new Counter; // calls the constructor Counter *c2 = new Counter(); // calls the constructor Counter c3; // calls the constructor Counter c4(); // calls the constructor
The parentensis () are optionional in this case. I prefer to use them to show visually that a constructor is called. You may do as you like.
Practice: Create a (dynamic reference to a) Daisy object and call its default constructor
Constructors with parameters
By this time at least one of you has heopfully already asked about it: What happens if we want to initialize variables, but we only know the values at construction? What if we need parameters to initialize the values to something that makes sense?
Example:
What should we use as starting values for x and y? 0 and 0 may be good values for a default constructor, but what we really want is a parameterized constructor where we can pass in the values.
Fortunately defining and implementing parameterized constructors is the same as default constructors, only that now we have parameters:
class Location { private: int x,y; public: Location(int, int); // ... }; ... Location::Location(int nx, int ny) { x = nx; y = ny; } //alternate: Location::Location(int x, int y) { this->x = x; this->y = y; }
Practice: Change "Daisy" to define an implement a constructor with one parameter (for number of leaves).
Just as with overloading (not overriding) we may have multiple constructors with different parameter lists. In most cases there will be a default constructor and one that takes parameters.
class Location { private: int x,y; public: Location() { x = 0; y = 0; } Location(int nx, int ny) { x = nx; y = ny; } Location(int d) { x = d; y = d; } // ... }; alternate, but not the SAME!!!!!! Location(int nx=0;int ny=0) { x = nx; y = ny; }
Calling a parameterized constructor:
We call parameterized constructors during object creation. Example:
Location *la = new Location(1,2); // for dynamic objects Location lb(4,5); // for static references
Practice: Create another new (dynamic) Daisy object and call its parameterized constructor.
Please note:
If we define no constructors, C++ will automatically provide an empty a default constructor.
If we define at least one constructor (default or parameterized) C++ will complain if we want to use the default constructor. This sometimes happen accidentaly.
Example:
Location l; // calls the default constructor // even though its not obvious
The copy constructor
This part is merely for completenes, but I will not ask you for that in any test. Assume Location object as given earlier, and all static object references.
Location la(1,2); Location lb; lb = la;
What happens here?
In the first line an object is created with the parameterized constructor.
In the secont line an object is created with the default constructor.
In the third line the object previousely referenced by lb is thrown away. A new object is created using the "copy constructor" to copy the variable values from la. This new object is then assigned to la.
C++ provides this copy constructor for us. If we want to override it we need to decare a constructor that takes one parameter whos type is a reference to the class type.
Example:
class Location { ... Location(const Location &l); // const is optional but recommended ... };