C++ Learning Manual – Object-Oriented Programming (OOP) 30 – Constructors and Destructors

Concept and Use of Constructors

A constructor is a special member function that has the same name as the class and has no return type, automatically executed when an object is created. The main purpose of a constructor is to initialize the data members of the object.

Default Constructor

When a class does not define any constructors, C++ provides an implicit default constructor. If the class defines other constructors, the compiler will no longer provide the default constructor, and it needs to be explicitly defined.

class Point {
public:
    Point() {  // Default constructor
        x = 0;
        y = 0;
    }
private:
    int x, y;
};
Parameterized Constructor

A constructor can accept parameters to initialize the object in different states.

class Point {
public:
    Point(int xVal, int yVal) {
        x = xVal;
        y = yVal;
    }
private:
    int x, y;
};
Initializer List

Using an initializer list can more efficiently initialize member variables, especially for const members and reference members.

class Point {
public:
    Point(int xVal, int yVal) : x(xVal), y(yVal) {}
private:
    int x, y;
};
Concept and Use of Destructors

A destructor is automatically called at the end of an object’s lifecycle to release the resources occupied by the object. The name of a destructor is prefixed with a tilde (~), and it has no parameters or return value.

class FileHandler {
public:
    FileHandler(const char* filename) {
        file = fopen(filename, "r");
    }
    
    ~FileHandler() {
        if (file) {
            fclose(file);
        }
    }
private:
    FILE* file;
};

Order of Constructor and Destructor Calls

During the creation and destruction of an object, the calls to constructors and destructors follow a specific order:

  1. Base class constructor
  2. Member object constructors (in declaration order)
  3. Derived class constructor

The order of destructor calls is the reverse of that of constructors:

  1. Derived class destructor
  2. Member object destructors (in reverse declaration order)
  3. Base class destructor
class Base {
public:
    Base() { cout << "Base constructor" << endl; }
    ~Base() { cout << "Base destructor" << endl; }
};

class Member {
public:
    Member() { cout << "Member constructor" << endl; }
    ~Member() { cout << "Member destructor" << endl; }
};

class Derived :public Base {
public:
    Derived() { cout << "Derived constructor" << endl; }
    ~Derived() { cout << "Derived destructor" << endl; }
private:
    Member mem;
};

Special Constructors

Copy Constructor

A copy constructor is used to create a new object from an existing object. If no copy constructor is defined, the compiler provides a default member-wise copy version.

class String {
public:
    String(const char* str) {
        size = strlen(str);
        data = new char[size + 1];
        strcpy(data, str);
    }
    
    String(const String& other) {  // Copy constructor
        size = other.size;
        data = new char[size + 1];
        strcpy(data, other.data);
    }
    
    ~String() {
        delete[] data;
    }
private:
    char* data;
    size_t size;
};
Move Constructor (C++11)

A move constructor allows the transfer of resource ownership, improving performance.

class String {
public:
    String(String&& other) noexcept {  // Move constructor
        data = other.data;
        size = other.size;
        other.data = nullptr;
        other.size = 0;
    }
};

Considerations for Constructors and Destructors

  1. Constructors can be overloaded, but destructors cannot be overloaded
  2. Constructors can have default parameters, but destructors cannot have parameters
  3. Constructors can throw exceptions, but resource allocation must be handled carefully
  4. Destructors should generally not throw exceptions
  5. For classes that contain dynamically allocated resources, the “Rule of Three” (copy constructor, copy assignment operator, destructor) should be followed

Conclusion

Constructors and destructors are fundamental to managing the lifecycle of C++ objects. Constructors ensure that objects are properly initialized, while destructors ensure that resources are correctly released. Understanding their call order and characteristics is crucial for writing robust C++ code. In complex class designs, especially those involving resource management, special attention must be paid to copy control and move semantics implementation.

Leave a Comment