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

results matching ""

    No results matching ""