C++ Interview Questions

22 Questions
C++ Programming

C++ Programming

Embedded SystemsIoTOther

Question 9

What is the Rule of Three in C++? Explain its importance and provide examples.

Answer:

The Rule of Three in C++ is a fundamental principle of object-oriented programming that dictates that if a class defines one of the following three special member functions, it should probably explicitly define all three: the destructor, the copy constructor, and the copy assignment operator. This rule helps manage resources such as dynamic memory, file handles, and network connections, ensuring that they are properly allocated and deallocated, preventing resource leaks and undefined behavior.

  1. Destructor:
    The destructor is responsible for releasing resources allocated by the class. If a class allocates dynamic memory or other resources in its constructor, it must release them in its destructor.

    class MyClass {
    private:
        int* data;
    public:
        MyClass(int size) {
            data = new int[size];
        }
        ~MyClass() {
            delete[] data;
        }
    };
  2. Copy Constructor:
    The copy constructor is called when a new object is created from an existing object. It ensures that a deep copy of the object's resources is made, rather than just copying the pointers, which could lead to double deletion and other issues.

    class MyClass {
    private:
        int* data;
        int size;
    public:
        MyClass(int size) : size(size) {
            data = new int[size];
        }
        MyClass(const MyClass& other) : size(other.size) {
            data = new int[size];
            std::copy(other.data, other.data + size, data);
        }
        ~MyClass() {
            delete[] data;
        }
    };
  3. Copy Assignment Operator:
    The copy assignment operator is used to copy the contents of one object to another existing object. Like the copy constructor, it ensures a deep copy of the object's resources.

    class MyClass {
    private:
        int* data;
        int size;
    public:
        MyClass(int size) : size(size) {
            data = new int[size];
        }
        MyClass(const MyClass& other) : size(other.size) {
            data = new int[size];
            std::copy(other.data, other.data + size, data);
        }
        MyClass& operator=(const MyClass& other) {
            if (this == &other) {
                return *this;
            }
            delete[] data;
            size = other.size;
            data = new int[size];
            std::copy(other.data, other.data + size, data);
            return *this;
        }
        ~MyClass() {
            delete[] data;
        }
    };

Importance:

  • Resource Management: Properly managing resources such as dynamic memory, file handles, and network connections is crucial for robust and efficient programs. The Rule of Three ensures that these resources are correctly copied and released, preventing resource leaks and other bugs.
  • Consistency: Ensuring that a class consistently manages its resources makes the code more predictable and easier to maintain. If one of the three functions is defined, it is a sign that special handling of resources is required, and defining all three functions ensures that this handling is consistently applied.
  • Avoiding Undefined Behavior: Without the Rule of Three, there is a risk of shallow copies, double deletions, and other forms of undefined behavior, leading to crashes and difficult-to-trace bugs.

Example:
Consider a class that manages a dynamic array:

#include <iostream>
#include <algorithm>

class DynamicArray {
private:
    int* data;
    int size;
public:
    DynamicArray(int size) : size(size) {
        data = new int[size];
    }

    DynamicArray(const DynamicArray& other) : size(other.size) {
        data = new int[size];
        std::copy(other.data, other.data + size, data);
    }

    DynamicArray& operator=(const DynamicArray& other) {
        if (this == &other) {
            return *this;
        }
        delete[] data;
        size = other.size;
        data = new int[size];
        std::copy(other.data, other.data + size, data);
        return *this;
    }

    ~DynamicArray() {
        delete[] data;
    }

    void print() const {
        for (int i = 0; i < size; ++i) {
            std::cout << data[i] << " ";
        }
        std::cout << std::endl;
    }
};

int main() {
    DynamicArray arr1(5);
    DynamicArray arr2 = arr1;
    DynamicArray arr3(3);
    arr3 = arr1;
    arr1.print();
    arr2.print();
    arr3.print();
    return 0;
}

In this example, the DynamicArray class follows the Rule of Three. It ensures that the dynamic memory allocated for the array is properly managed, with deep copies made during construction and assignment, and memory is released when the object is destroyed. This prevents memory leaks and undefined behavior, making the code robust and maintainable.

Recent job openings