Constructors_destructors
Constructors and Destructors
Section titled “Constructors and Destructors”Constructors and destructors are special member functions that manage object creation and destruction. Understanding them is crucial for proper resource management in C++.
Constructor Basics
Section titled “Constructor Basics”A constructor is called when an object is created. It initializes the object’s state.
class Person {private: std::string name; int age;
public: // Constructor - same name as class, no return type Person(const std::string& n, int a) { name = n; age = a; }};
int main() { Person p("Alice", 30); // Constructor is called}Types of Constructors
Section titled “Types of Constructors”Default Constructor
Section titled “Default Constructor”Constructor with no parameters:
class Rectangle { double width, height;
public: // Default constructor Rectangle() { width = 1; height = 1; }};
Rectangle r1; // Uses default constructorRectangle r2(); // Function declaration!Rectangle r3{}; // Preferred C++11 syntaxParameterized Constructor
Section titled “Parameterized Constructor”Constructor with parameters:
class Rectangle { double width, height;
public: Rectangle(double w, double h) { width = w; height = h; }};
Rectangle r(5, 3);Copy Constructor
Section titled “Copy Constructor”Creates a new object as a copy of an existing one:
class Rectangle { double width, height;
public: Rectangle(double w, double h) : width(w), height(h) {}
// Copy constructor Rectangle(const Rectangle& other) { width = other.width; height = other.height; }};
Rectangle r1(5, 3);Rectangle r2(r1); // Copy constructorRectangle r3 = r1; // Also calls copy constructorMove Constructor (C++11)
Section titled “Move Constructor (C++11)”Transfers ownership of resources:
class Buffer { char* data; size_t size;
public: Buffer(size_t s) : size(s) { data = new char[size]; }
// Move constructor Buffer(Buffer&& other) noexcept { data = other.data; size = other.size; other.data = nullptr; other.size = 0; }
~Buffer() { delete[] data; }};Constructor Overloading
Section titled “Constructor Overloading”Multiple constructors with different parameters:
class Rectangle { double width, height;
public: // Default constructor Rectangle() : width(1), height(1) {}
// Parameterized constructor Rectangle(double w, double h) : width(w), height(h) {}
// Square constructor Rectangle(double side) : width(side), height(side) {}};
Rectangle r1; // Default: 1x1Rectangle r2(5, 3); // 5x3Rectangle r3(4); // 4x4 (square)Initializer List
Section titled “Initializer List”The preferred way to initialize members:
class Person {private: const std::string name; // Must be initialized int age; double salary;
public: // Using initializer list Person(const std::string& n, int a, double s) : name(n), age(a), salary(s) { // Constructor body can be empty }};
// Bad way (assignment, not initialization)Person(const std::string& n, int a, double s) { name = n; // Error! const member age = a; salary = s;}Why use initializer lists?
- Required for
constand reference members - More efficient (avoids extra assignment)
- Required for base classes and member objects without default constructors
Delegating Constructors (C++11)
Section titled “Delegating Constructors (C++11)”One constructor calls another:
class Rectangle { double width, height;
public: Rectangle() : Rectangle(1, 1) {} // Delegate to parameterized
Rectangle(double w, double h) : width(w), height(h) {}
Rectangle(double side) : Rectangle(side, side) {} // Square};Destructor Basics
Section titled “Destructor Basics”A destructor is called when an object is destroyed. It cleans up resources:
class Resource {public: Resource() { std::cout << "Acquired\n"; } ~Resource() { std::cout << "Released\n"; }};
int main() { Resource r; // Constructor called std::cout << "Doing work\n"; return 0; // Destructor called when r goes out of scope}Output:
AcquiredDoing workReleasedWhen is Destructor Called?
Section titled “When is Destructor Called?”- Local variable goes out of scope
- Object is deleted with
delete - Program ends (for global/static objects)
Virtual Destructors
Section titled “Virtual Destructors”Always make base class destructors virtual when using polymorphism:
class Base {public: virtual ~Base() { std::cout << "Base destructor\n"; }};
class Derived : public Base {public: ~Derived() { std::cout << "Derived destructor\n"; }};
int main() { Base* ptr = new Derived(); delete ptr; // Both destructors called}Without virtual destructor:
Base destructor // Only base destructor called!With virtual destructor:
Derived destructorBase destructorRule of Three/Five/Zero
Section titled “Rule of Three/Five/Zero”Rule of Three (C++98)
Section titled “Rule of Three (C++98)”If you define any of these, define all three:
- Destructor
- Copy constructor
- Copy assignment operator
class MyClass { int* data;
public: MyClass() : data(new int[100]) {}
// Destructor ~MyClass() { delete[] data; }
// Copy constructor MyClass(const MyClass& other) : data(new int[100]) { std::copy(other.data, other.data + 100, data); }
// Copy assignment MyClass& operator=(const MyClass& other) { if (this != &other) { delete[] data; data = new int[100]; std::copy(other.data, other.data + 100, data); } return *this; }};Rule of Five (C++11)
Section titled “Rule of Five (C++11)”Add move constructor and move assignment:
class MyClass { int* data;
public: MyClass() : data(new int[100]) {}
// Destructor ~MyClass() { delete[] data; }
// Copy constructor MyClass(const MyClass& other) : data(new int[100]) { std::copy(other.data, other.data + 100, data); }
// Copy assignment MyClass& operator=(const MyClass& other) { if (this != &other) { delete[] data; data = new int[100]; std::copy(other.data, other.data + 100, data); } return *this; }
// Move constructor MyClass(MyClass&& other) noexcept : data(other.data) { other.data = nullptr; }
// Move assignment MyClass& operator=(MyClass&& other) noexcept { if (this != &other) { delete[] data; data = other.data; other.data = nullptr; } return *this; }};Rule of Zero (C++11 and later)
Section titled “Rule of Zero (C++11 and later)”Prefer not defining any - use RAII classes (like smart pointers):
class MyClass { std::vector<int> data; // Manages its own memory std::unique_ptr<Resource> resource;
public: // Compiler-generated special member functions work!};Explicit Constructors
Section titled “Explicit Constructors”Prevent implicit conversions with explicit:
class MyInt { int value;
public: explicit MyInt(int v) : value(v) {}};
void printInt(MyInt m) { std::cout << m.getValue() << std::endl;}
int main() { printInt(42); // Error: explicit prevents implicit printInt(MyInt(42)); // OK: explicit call}Complete Example
Section titled “Complete Example”#include <iostream>#include <string>#include <vector>
class Person {private: std::string name; int age; std::vector<std::string> hobbies;
public: // Default constructor Person() : name("Unknown"), age(0) { std::cout << "Default constructor\n"; }
// Parameterized constructor Person(const std::string& n, int a) : name(n), age(a) { std::cout << "Parameterized constructor: " << name << "\n"; }
// Copy constructor Person(const Person& other) : name(other.name), age(other.age), hobbies(other.hobbies) { std::cout << "Copy constructor: " << name << "\n"; }
// Move constructor Person(Person&& other) noexcept : name(std::move(other.name)), age(other.age), hobbies(std::move(other.hobbies)) { std::cout << "Move constructor: " << name << "\n"; }
// Destructor ~Person() { std::cout << "Destructor: " << name << "\n"; }
// Copy assignment Person& operator=(const Person& other) { if (this != &other) { name = other.name; age = other.age; hobbies = other.hobbies; } return *this; }
void addHobby(const std::string& hobby) { hobbies.push_back(hobby); }
void display() const { std::cout << "Name: " << name << ", Age: " << age; std::cout << ", Hobbies: "; for (const auto& h : hobbies) std::cout << h << " "; std::cout << "\n"; }};
int main() { std::cout << "=== Creating p1 ===\n"; Person p1("Alice", 25); p1.addHobby("Reading");
std::cout << "\n=== Copying p1 to p2 ===\n"; Person p2 = p1; // Copy constructor
std::cout << "\n=== Creating p3 ===\n"; Person p3("Bob", 30);
std::cout << "\n=== Moving p3 to p4 ===\n"; Person p4 = std::move(p3);
std::cout << "\n=== End of main ===\n"; return 0;}Output:
=== Creating p1 ===Parameterized constructor: Alice
=== Copying p1 to p2 ===Copy constructor: Alice
=== Creating p3 ===Parameterized constructor: Bob
=== Moving p3 to p4 ===Move constructor: Bob
=== End of main ===Destructor: BobDestructor: AliceDestructor: AliceKey Takeaways
Section titled “Key Takeaways”- Constructors initialize objects when they’re created
- Destructors clean up resources when objects are destroyed
- Types: default, parameterized, copy, move constructors
- Use initializer lists for efficient and proper initialization
- Make base class destructors virtual for polymorphic deletion
- Follow the Rule of Three/Five/Zero
- Use
explicitto prevent unintended implicit conversions
Next Steps
Section titled “Next Steps”Now let’s learn about inheritance, one of the pillars of OOP.
Next Chapter: 10_inheritance.md - Inheritance and Composition