Study resource

Read at your pace, then save it for later.

Unit 12: Advanced Concepts: Constructors and Destructors in C++

Constructors with Default Arguments

In C++, constructors can be overloaded, meaning a class can have multiple constructors with different parameter lists. Constructors can also be defined with default arguments, which provide flexibility when creating objects.

A default argument is a value given to a parameter in a function (or constructor) declaration. If the caller does not provide an argument for that parameter, the default value is used. If an argument is provided, the default value is overridden.

When a constructor has default arguments, you can call it with fewer arguments than it is defined to accept. The missing arguments will automatically take on their default values.

How Default Arguments Work with Constructors

Consider a constructor defined as follows:

class MyClass {public:    int a, b;    MyClass(int x = 0, int y = 0) {        a = x;        b = y;    }};

In this example, the constructor MyClass(int x = 0, int y = 0) has two parameters, x and y, both with default values of 0. This single constructor can be used to create objects in several ways:

  • No arguments: MyClass obj1; // obj1.a becomes 0, obj1.b becomes 0
  • One argument: MyClass obj2(10); // obj2.a becomes 10, obj2.b becomes 0
  • Two arguments: MyClass obj3(10, 20); // obj3.a becomes 10, obj3.b becomes 20

Important Observations:

  • Once a default argument is provided for a parameter, all subsequent parameters in the same function declaration must also have default arguments. For example, MyClass(int x, int y = 0, int z) is illegal. It should be MyClass(int x, int y = 0, int z = 0).
  • Default arguments are typically specified in the function declaration (prototype) rather than the definition, especially when the definition is in a separate .cpp file.
  • Using default arguments can reduce the need for multiple overloaded constructors, making the code more concise and easier to maintain.

Dynamic Initialization of Objects

Normally, objects are created and initialized when their declaration is encountered during program execution. However, C++ also allows dynamic initialization of objects, where objects are created and initialized during runtime using pointers and the new operator.

Dynamic initialization of objects refers to the process of creating and initializing objects on the heap (dynamic memory) at runtime, rather than compile time. This is primarily achieved using the new operator, which allocates memory and invokes the constructor.

The new operator allocates memory for an object on the heap and then calls the appropriate constructor to initialize that object. This is particularly useful when the exact number of objects or their specific types are not known until the program is running.

Syntax for Dynamic Initialization:

ClassName* ptr = new ClassName(arguments);

Here, ptr is a pointer to an object of ClassName. The new operator:

  • Allocates memory on the heap for an object of ClassName.
  • Calls the constructor ClassName(arguments) to initialize the newly allocated memory.
  • Returns a pointer to the newly created and initialized object.

Example:

class Point {public:    int x, y;    Point(int px = 0, int py = 0) {        x = px;        y = py;    }};Point* p1 = new Point(); // Dynamically creates a Point object with x=0, y=0Point* p2 = new Point(5, 10); // Dynamically creates a Point object with x=5, y=10

Important Note: Objects created with new must be explicitly deallocated using the delete operator to prevent memory leaks. The delete operator calls the object's destructor before deallocating the memory.

delete p1;delete p2;

Destructors

While constructors are special member functions used to initialize objects, destructors are special member functions responsible for cleaning up resources when an object is destroyed.

A destructor is a special member function of a class that is automatically invoked when an object is about to be destroyed. Its primary purpose is to deallocate any resources (like dynamically allocated memory, file handles, network connections) that the object might have acquired during its lifetime.

Characteristics of Destructors:

  • Naming: A destructor has the same name as the class, prefixed with a tilde (~). For example, the destructor for MyClass is ~MyClass().
  • No Return Type: Destructors do not have a return type, not even void.
  • No Arguments: Destructors cannot take any arguments. Therefore, a class can have only one destructor.
  • Automatic Invocation: Destructors are called automatically by the compiler when an object goes out of scope, or when delete is used for dynamically allocated objects.
  • Purpose: They are crucial for releasing resources that were allocated by the object during its lifetime to prevent resource leaks (e.g., memory leaks, file handle leaks).

When Destructors are Called:

  1. When a local object goes out of scope (e.g., end of a function block).
  2. When a dynamically allocated object is explicitly deallocated using the delete operator.
  3. When a temporary object is destroyed.
  4. When the program ends, for static and global objects.

Example:

class DynamicArray {public:    int* arr;    int size;    DynamicArray(int s) {        size = s;        arr = new int[size]; // Allocate memory        // Initialize array elements (not shown for brevity)        // ...        std::cout << "Constructor called, array of size " << size << " created." << std::endl;    }    ~DynamicArray() {        delete[] arr; // Deallocate memory        std::cout << "Destructor called, memory released." << std::endl;    }};void myFunction() {    DynamicArray obj1(5); // Constructor called, obj1 created} // obj1 goes out of scope here, destructor calledint main() {    DynamicArray* obj2 = new DynamicArray(10); // Constructor called, obj2 created    delete obj2; // Explicitly call destructor, obj2 destroyed    myFunction();    return 0;}

In this example, the destructor ~DynamicArray() is responsible for releasing the memory allocated by new int[size] in the constructor. Failing to provide a destructor to deallocate this memory would lead to a memory leak.

Order of Destruction:

  • For objects within the same scope, destructors are called in the reverse order of their construction.
  • For base and derived classes, the derived class destructor is called first, followed by the base class destructor.

xxx

Did this help you understand better?

Your feedback improves the quality of this resource for everyone.