一、对象克隆
在 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,减少了不必要的锁竞争,提升了性能,不过要注意编译器可能的指令重排问题,在实际更严谨的实现中,可能需要使用特定的内存屏障等机制来确保正确