对象克隆与单例模式

发布于:2024-12-19 ⋅ 阅读:(14) ⋅ 点赞:(0)

一、对象克隆

在 C++ 中,对象克隆通常可以借助拷贝构造函数和赋值运算符重载来实现,分为浅拷贝(默认行为)和深拷贝(需要自定义实现)。

1. 浅拷贝示例

#include <iostream>
#include <string>

class Person {
public:
    Person(const std::string& name, int age) : name(name), age(age) {}

// 编译器默认生成的拷贝构造函数实现的是浅拷贝
    Person(const Person& other) : name(other.name), age(other.age) {}

void printInfo() const {
        std::cout << "Name: " << name << ", Age: " << age << std::endl;
    }

private:
    std::string name;
    int age;
};

int main() {
    Person p1("Alice", 20);
    Person p2 = p1;  // 调用拷贝构造函数进行浅拷贝
    p2.printInfo();
    return 0;
}

在上述代码中,当使用Person p2 = p1;时,编译器会调用默认生成的拷贝构造函数,它简单地将p1对象的各成员变量的值复制给p2对象对应成员变量,对于像std::string这样的类类型(它内部已经处理好了深拷贝相关逻辑)能正常工作,但如果成员变量是指针等情况,就可能出现问题(多个对象的指针指向同一块内存区域,后续修改会相互影响),这就是浅拷贝的特点。

2. 深拷贝示例

假设Person类有一个指针成员变量指向动态分配的内存,就需要自定义拷贝构造函数和赋值运算符重载来实现深拷贝,如下:

#include <iostream>
#include <string>
#include <cstring>

class Person {
public:
    Person(const std::string& name, int age) : name(name), age(age) {
        this->hobby = new char[100];
        std::strcpy(this->hobby, "Reading");
    }

// 自定义拷贝构造函数实现深拷贝
    Person(const Person& other) : name(other.name), age(other.age) {
        this->hobby = new char[100];
        std::strcpy(this->hobby, other.hobby);
    }

// 赋值运算符重载实现深拷贝
    Person& operator=(const Person& other) {
        if (this!= &other) {
            this->name = other.name;
            this->age = other.age;
            delete[] this->hobby;
            this->hobby = new char[100];
            std::strcpy(this->hobby, other.hobby);
        }
        return *this;
    }

void printInfo() const {
        std::cout << "Name: " << name << ", Age: " << age << ", Hobby: " << hobby << std::endl;
    }

~Person() {
        delete[] hobby;
    }

private:
    std::string name;
    int age;
    char* hobby;
};

int main() {
    Person p1("Alice", 20);
    Person p2 = p1;  // 调用自定义拷贝构造函数进行深拷贝
    p2.printInfo();
    Person p3("Bob", 25);
    p3 = p1;  // 调用赋值运算符重载进行深拷贝
    p3.printInfo();
    return 0;
}

这里Person类有一个char*类型的hobby成员变量指向动态分配的内存,为了实现深拷贝,在拷贝构造函数和赋值运算符重载中都重新分配了内存,并将对应内容复制过来,避免多个对象的该指针指向同一块内存而导致修改相互影响的问题。

二、单例模式

1. 饿汉式单例模式

#include <iostream>

class Singleton {
public:
    static Singleton& getInstance() {
        return instance;
    }

void showMessage() const {
        std::cout << "This is a singleton instance." << std::endl;
    }

private:
    // 在程序启动时就创建好实例
    static Singleton instance;
    Singleton() {}  // 构造函数私有,防止外部创建实例
    Singleton(const Singleton&) = delete;  // 禁用拷贝构造函数
    Singleton& operator=(const Singleton&) = delete;  // 禁用赋值运算符
};

// 初始化静态成员变量
Singleton Singleton::instance;

int main() {
    Singleton::getInstance().showMessage();
    return 0;
}

饿汉式单例模式在程序启动时就创建好单例对象,优点是线程安全(由 C++ 语言本身保证静态变量初始化的线程安全性),缺点是如果单例对象构造比较复杂或者占用资源多,会在程序启动时就占用相应资源,即便暂时不用也会提前创建。

2. 懒汉式单例模式(线程安全版本,使用互斥锁)

#include <iostream>
#include <mutex>

class Singleton {
public:
    static Singleton& getInstance() {
        std::lock_guard<std::mutex> guard(mutex_);
        if (!instance) {
            instance = new Singleton();
        }
        return *instance;
    }

void showMessage() const {
        std::cout << "This is a singleton instance." << std::endl;
    }

private:
    static Singleton* instance;
    static std::mutex mutex_;
    Singleton() {}
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
};

// 初始化静态成员变量
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mutex_;

int main() {
    Singleton::getInstance().showMessage();
    return 0;
}

懒汉式单例模式是在第一次调用getInstance方法时才创建单例对象,为了保证线程安全,使用std::mutex互斥锁来保证在多线程环境下只有一个线程能创建实例。不过使用互斥锁会有一定性能开销,每次获取实例都需要获取锁判断是否已经创建。

3. 双重检查锁定(DCL)单例模式(线程安全且优化性能)

#include <iostream>
#include <mutex>

class Singleton {
public:
    static Singleton& getInstance() {
        if (!instance) {
            std::lock_guard<std::mutex> guard(mutex_);
            if (!instance) {
                instance = new Singleton();
            }
        }
        return *instance;
    }

void showMessage() const {
        std::cout << "This is a singleton instance." << std::endl;
    }

private:
    static Singleton* instance;
    static std::mutex mutex_;
    Singleton() {}
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
};

// 初始化静态成员变量
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mutex_;

int main() {
    Singleton::getInstance().showMessage();
    return 0;
}

双重检查锁定模式在懒汉式基础上进行优化,通过两次检查instance是否为nullptr,减少了不必要的锁竞争,提升了性能,不过要注意编译器可能的指令重排问题,在实际更严谨的实现中,可能需要使用特定的内存屏障等机制来确保正确