Constructor and Deconstructor
This chapter will introduce what is the:
- default constructor
- copy constructor
- move constructor
And we will discuss
- How to use them
- When should we use them
Example of constructor and deconstructor:
class Demo
{
public:
Demo() // <-- This is constructor
{
// Do something when Demo object is created...
}
~Demo() // <-- This is deconstructor
{
// Do something when Demo object is deleted...
}
};
What constructor and deconstructor does
Constructor
- Initialize member variables
- Convert data type
- Allocate memory
- Check the correctness of the passed arguments(e.g. assert something)
Deconstructor
- Deallocate memory
Example of constructor and deconstructor
The following code will show us how constructor and deconstructor does.
#include <iostream>
#include <cassert>
#include <cmath>
using std::cout;
using std::endl;
using std::ostream; // for << operator overloading
#define DEBUG true
void log(std::string s) {
DEBUG && (cout << "[" << __FILE__ << "]" << s << "\n");
}
class Water
{
public:
Water(double aTemperature, double aVolume)
{
log("Water");
// The water's temperature must be in 0 ~ 100 celsius.
// If it's under 0, then it will be ice. If it's over 100, it will be vapor.
assert(aTemperature <= 100 && aTemperature >= 0);
mTemperature = aTemperature;
mVolume = aVolume;
mNumBuckets = static_cast<int>(ceil(mVolume));
mBuckets = new int[mNumBuckets];
}
// Overwrite the output stream
friend ostream& operator<<(ostream& out, const Water& w)
{
out << "Temperature: " << w.mTemperature
<< ", Volume: " << w.mVolume
<< ", Buckets: " << w.mNumBuckets;
return out;
}
~Water()
{
log("~Water");
delete[] mBuckets;
}
private:
double mTemperature; // Centigrade
double mVolume; // Liter
int* mBuckets; // 1L per bucket
int mNumBuckets;
};
int main() {
Water w(78.3, 4.3);
cout << w << endl;
return 0;
}
In the Water
's constructor, the first thing we do
is to make sure the argument aTemperature
is in the correct range.
This is extremely useful for debugging.
If Water
is used by others,
we can know there is something wrong before creating the Water
object.
It's helpful to narrow down the range of incorrect codes.
After the assertion is passed, the member variables can be initialized.
One interesting thing in the constructer is that we need to allocate memory
for mBuckets
dynamically. Its size is determined by mVolume
.
Before allocating memory for mBuckets
, we need to cast the mVolume
to an appropriate number called mNumBuckets
,
and then use it as the number of memory chunks.
Due to the memory asked in constructor, we need to give them back to heap in deconstructor. Otherwise, the memory leak happens. It will not only consume the memory space you can use and slow down the speed of the following memory allocating.
This is inefficient, and this is serious problem when you use C++. Efficiency is the most important property in C++. Without it, why not JAVA.
The output of above is:
[constructor.cpp]Water
Temperature: 78.3, Volume: 4.3, Buckets: 5
[constructor.cpp]~Water