Monday, December 9, 2024

C++ Constructor/ Copy Constructor / Assignment Operator (=)

In C++, a singleton class ensures that only one instance of the class is created throughout the program. To achieve this, we use private constructors, disable copy operations, and disable assignment operations. Here’s why each step is necessary:


Key Elements of a Singleton Class

  1. Private Constructor:

    • Ensures that instances cannot be created directly using the new operator or stack allocation.
  2. Private Copy Constructor:

    • Prevents copying of the singleton instance.
    • If copying were allowed, it would defeat the purpose of a singleton, as multiple instances would be created.
  3. Private Assignment Operator (=):

    • Prevents assigning one instance of the singleton to another, which could lead to multiple instances or unintended behaviors.

Why Are Copy Constructor and Assignment Operator Needed?

  1. Copy Constructor:

    • By default, C++ provides a copy constructor that performs a shallow copy of the object.
    • If we don’t explicitly delete or make the copy constructor private, users can create copies of the singleton instance, violating its single-instance guarantee.
    • Example of misuse:
      Singleton s1 = Singleton::getInstance();
      Singleton s2 = s1; // Creates a copy of the singleton!
      
  2. Assignment Operator:

    • By default, C++ provides an assignment operator (=) that allows copying data from one instance to another.
    • If not explicitly deleted or made private, users can assign one singleton instance to another, leading to multiple logically identical objects.
    • Example of misuse:
      Singleton s1 = Singleton::getInstance();
      Singleton s2;     // Another instance
      s2 = s1;          // Copies the singleton instance into `s2`
      

To ensure the singleton pattern remains intact, both the copy constructor and assignment operator are disabled.


Example of a Singleton Class in C++

#include <iostream>

class Singleton {
public:
    // Static method to access the single instance
    static Singleton& getInstance() {
        static Singleton instance; // Single instance
        return instance;
    }

    // Public method for demonstration
    void display() const {
        std::cout << "Singleton instance at: " << this << std::endl;
    }

    // Delete copy constructor and assignment operator
    Singleton(const Singleton&) = delete;            // No copy constructor
    Singleton& operator=(const Singleton&) = delete; // No assignment operator

private:
    // Private constructor
    Singleton() {
        std::cout << "Singleton constructor called!" << std::endl;
    }

    // Private destructor (optional)
    ~Singleton() = default;
};

int main() {
    // Access the single instance
    Singleton& s1 = Singleton::getInstance();
    s1.display();

    Singleton& s2 = Singleton::getInstance();
    s2.display();

    // Uncommenting the lines below will cause compilation errors
    // Singleton s3 = s1;          // Copy constructor is deleted
    // Singleton s4;
    // s4 = s1;                   // Assignment operator is deleted

    return 0;
}

Output

Singleton constructor called!
Singleton instance at: 0x7ffeea58c6f8
Singleton instance at: 0x7ffeea58c6f8

Summary

  • Private constructor: Prevents external instantiation.
  • Deleted copy constructor: Prevents creating a new instance by copying an existing one.
  • Deleted assignment operator: Prevents assigning one instance to another, which could overwrite and violate the singleton’s integrity.

Disabling these guarantees that only one instance of the singleton class exists and that its lifetime is properly controlled.


Singleton Class with Public Copy Constructor and Assignment Operator

Code

#include <iostream>

class Singleton {
public:
    // Static method to access the single instance
    static Singleton& getInstance() {
        static Singleton instance;
        return instance;
    }

    // Public methods for demonstration
    void display() const {
        std::cout << "Singleton instance at: " << this << std::endl;
    }

    // Public copy constructor
    Singleton(const Singleton& other) {
        std::cout << "Copy constructor called!" << std::endl;
    }

    // Public assignment operator
    Singleton& operator=(const Singleton& other) {
        std::cout << "Assignment operator called!" << std::endl;
        return *this;
    }

private:
    // Private constructor
    Singleton() {
        std::cout << "Singleton constructor called!" << std::endl;
    }

    // Private destructor
    ~Singleton() = default;
};

int main() {
    // Access the single instance
    Singleton& s1 = Singleton::getInstance();
    s1.display();

    // Copy constructor misuse
    Singleton s2 = s1; // Creates a new instance via copy constructor
    s2.display();

    // Assignment operator misuse
    Singleton s3 = Singleton::getInstance(); // Another reference to the singleton
    s3 = s1; // Copies the instance via assignment operator

    s3.display(); // Display to check its address
    return 0;
}

Output

Singleton constructor called!
Singleton instance at: 0x7ffeea58c6f8
Copy constructor called!
Singleton instance at: 0x7ffeea58c6f0 - new address
Assignment operator called!
Singleton instance at: 0x7ffeea58c6f8

Explanation

  1. Singleton Constructor:

    • Called once during Singleton::getInstance(). Creates the singleton instance (s1) at memory address 0x7ffeea58c6f8.
  2. Copy Constructor:

    • Called during Singleton s2 = s1;.
    • Creates a new instance of the singleton (s2) with a different memory address (0x7ffeea58c6f0).
  3. Assignment Operator:

    • Called during s3 = s1;.
    • Copies the state of s1 into s3, but since both s3 and s1 are references to the same singleton instance, no new memory is allocated.
    • Both s1 and s3 continue to refer to the singleton instance at 0x7ffeea58c6f8.
  4. Final Display (s3.display()):

    • Outputs the memory address of s3, which is the same as s1 (0x7ffeea58c6f8).

Key Learning

  • The copy constructor creates a new instance of the class, breaking the singleton pattern.
  • The assignment operator does not create a new instance but allows modifying the state of the singleton, which can lead to resource duplication or corruption in real-world scenarios.

No comments:

Post a Comment

LeetCode C++ Cheat Sheet June

🎯 Core Patterns & Representative Questions 1. Arrays & Hashing Two Sum – hash map → O(n) Contains Duplicate , Product of A...