SrcForge

Lesson 29

Constructors and Destructors

Covers constructor and destructor syntax, object lifecycle, default, parameterized, and copy constructors, constructor overloading, and destructor rules.

Lesson content

What are constructors and destructors?

A constructor is a special member function that is automatically called when an object is created. A destructor is a special member function that is automatically called when an object is destroyed.

  • The constructor prepares your room when you check in
  • The destructor cleans up the room when you check out

Why do they exist?

Without constructors and destructors, objects may contain uninitialized data, and resources such as memory or files may not be released.

  • Automatically initializing objects
  • Automatically cleaning up resources
  • Supporting the RAII (Resource Acquisition Is Initialization) programming technique

Real-world use cases

  • Database connections
  • File handling
  • Dynamic memory management
  • Network socket management
  • Game object initialization
  • Resource management
  • Logging systems

Prerequisites

  • Classes and objects
  • Member functions
  • Access specifiers
  • Variables
  • Basic C++ syntax

What is a constructor?

  • Has the same name as the class
  • Has no return type, not even void
  • Is called automatically when an object is created
  • Initializes data members
class Student
{
public:
    Student()
    {
        // Constructor body
    }
};

What is a destructor?

  • Has the same name as the class, prefixed with ~
  • Has no return type
  • Takes no parameters
  • Is called automatically when an object is destroyed
class Student
{
public:
    ~Student()
    {
        // Cleanup code
    }
};

Object lifecycle

  • Object creation
  • Constructor executes
  • Object is used
  • Destructor executes
  • Object removed

Default constructor

A constructor with no parameters.

class Student
{
public:
    Student()
    {
        cout << "Default Constructor";
    }
};

Parameterized constructor

Accepts parameters to initialize an object.

class Student
{
public:
    Student(string name)
    {
        cout << name;
    }
};

Copy constructor

Creates a new object as a copy of an existing object.

ClassName(const ClassName& other);

// Example:
Student(const Student& other)
{
    name = other.name;
}

If you don't define one, the compiler generates a default copy constructor.

Constructor overloading

A class can have multiple constructors with different parameter lists.

Student();

Student(string);

Student(string, int);

The compiler selects the appropriate constructor based on the arguments.

Destructor rules

A class can have only one destructor.

  • Cannot be overloaded
  • Cannot take parameters
  • Is called automatically

Example 1: Default constructor

#include <iostream>
using namespace std;

class Student
{
public:

    Student()
    {
        cout << "Student object created." << endl;
    }
};

int main()
{
    Student student;

    return 0;
}

// Output: Student object created.

Example 2: Parameterized constructor

#include <iostream>
#include <string>
using namespace std;

class Student
{
public:

    string name;

    Student(string studentName)
    {
        name = studentName;
    }

    void display()
    {
        cout << name << endl;
    }
};

int main()
{
    Student student("Alice");

    student.display();

    return 0;
}

// Output: Alice

Example 3: Constructor overloading

#include <iostream>
#include <string>
using namespace std;

class Employee
{
public:

    string name;

    Employee()
    {
        name = "Unknown";
    }

    Employee(string employeeName)
    {
        name = employeeName;
    }

    void display()
    {
        cout << name << endl;
    }
};

int main()
{
    Employee employee1;

    Employee employee2("David");

    employee1.display();

    employee2.display();

    return 0;
}

// Output:
// Unknown
// David

Example 4: Constructor and destructor

#include <iostream>
using namespace std;

class Test
{
public:

    Test()
    {
        cout << "Constructor called." << endl;
    }

    ~Test()
    {
        cout << "Destructor called." << endl;
    }
};

int main()
{
    Test object;

    cout << "Using object..." << endl;

    return 0;
}

// Output:
// Constructor called.
// Using object...
// Destructor called.

Example 5: Managing dynamic memory

#include <iostream>
using namespace std;

class Array
{
private:

    int* data;

public:

    Array(int size)
    {
        data = new int[size];

        cout << "Memory allocated." << endl;
    }

    ~Array()
    {
        delete[] data;

        cout << "Memory released." << endl;
    }
};

int main()
{
    Array numbers(5);

    return 0;
}

// Output:
// Memory allocated.
// Memory released.

Common mistakes and pitfalls

  • Giving a constructor a return type (void Student()). Fix: constructors have no return type
  • Giving a destructor parameters. Fix: destructors cannot take parameters
  • Forgetting to release resources allocated with new. Fix: release them in the destructor or use smart pointers
  • Performing complex work that may throw exceptions in a destructor. Fix: keep destructors simple and avoid throwing exceptions
  • Assigning values inside the constructor body when initializer lists are more appropriate. Fix: prefer constructor initializer lists
// Example of an initializer list:
class Student
{
private:
    int id;

public:
    Student(int studentId) : id(studentId)
    {
    }
};

Best practices

  • Initialize data members using constructor initializer lists
  • Keep constructors focused on object initialization
  • Keep destructors focused on cleanup
  • Avoid manual resource management when possible by using RAII and smart pointers
  • Ensure every resource acquired by a class is properly released
  • Follow the Rule of Three/Five/Zero when managing resources

When not to use this

  • The compiler-generated versions are sufficient
  • The class contains only standard library types that manage their own resources
  • You can rely on the Rule of Zero, where no custom resource management is needed

Summary

  • A constructor initializes an object automatically when it is created
  • A destructor cleans up resources automatically when the object is destroyed
  • Constructors have the same name as the class and no return type
  • Destructors begin with ~, have no parameters, and cannot be overloaded
  • Constructors can be overloaded
  • Use constructor initializer lists for efficient initialization
  • Destructors are essential for releasing resources such as dynamically allocated memory
  • Prefer RAII and smart pointers in modern C++ to reduce manual resource management

Frequently asked questions

What is the difference between a constructor and a destructor?

A constructor initializes an object and is called when an object is created; it can be overloaded. A destructor cleans up an object and is called when an object is destroyed; it cannot be overloaded.

Can a constructor have a return type?

No. Constructors never have a return type, not even void.

Can a class have multiple constructors?

Yes. This is called constructor overloading, where each constructor has a different parameter list.

When is a destructor called?

A destructor is called automatically when a local object goes out of scope, an object created with new is deleted, or a program terminates and destroys static objects.

Why are constructors and destructors important?

They automate object initialization and cleanup, making programs safer, easier to maintain, and less prone to resource leaks. They are a key part of writing reliable C++ code.