上节我们完成了一个单例工厂类,单例和工厂合到一起了。本节我们将分别实现它们,便于代码重用。OKay,AI助手上场。
mzhai: Write a class to implement singleton design pattern using modern c++.
bard:?Here's a C++ class implementing the singleton design pattern using modern techniques:
#include <mutex>
class Singleton {
public:
static Singleton& getInstance() {
std::lock_guard<std::mutex> lock(mutex); // Acquire lock for thread safety
static Singleton instance; // Local static variable for thread safety
return instance;
}
// Prevent copying and moving to ensure a single instance
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
Singleton(Singleton&&) = delete;
Singleton& operator=(Singleton&&) = delete;
protected:
Singleton() = default; // Private constructor for singleton
private:
static std::mutex mutex; // Mutex for thread-safe initialization
};
// Initialize the static mutex member outside the class
std::mutex Singleton::mutex;
关于单例模式,比较简单而且这个模式很成熟,网上有大量资料。所以bard这个问题回答的很好,
只是有一点有点怀疑:static Singleton instance需要锁保护吗?要回答这个问题,首先必须知道这个变量什么时候初始化?
1.在C++中,全局static变量和class的static成员变量在main函数之前初始化,main函数之后销毁。
2. 函数内部的局部static变量在该函数第一次被调用时初始化,在main函数之后销毁
3.对于static数据即使是函数内部的对象则存放在全局数据区,全局数据区的数据并不会因为函数的退出就将空间释放。
如果两个线程同时调用Singleton::getInstance会同时初始化Singleton instance,可能会造成问题。但是这一点,大牛们早就想到了,这里不必加锁,AI有点画蛇添足了。这个问题我们以后再细细研究。
还有一个问题:Singleton instance是在函数getInstance已进入就初始化?还是运行到这一句才初始化?通过一个小小的试验就会有答案:
class Product {
public:
Product(){std::cout<<"Product constructor"<<std::endl;}
};
void test(){
std::cout<<"I am in test()"<<std::endl;
static Product pro;
std::cout<<"leaving test()"<<std::endl;
}
int main() {
test();
test();
return 0;
}
为什么要问这个问题哪?因为如果函数内static变量一上来就初始化(跳到所有函数内语句之前)将会造成lock时机晚了。还好没有这个问题。
有了上面的单例类,我们的工厂类就可以去掉getInstance()函数了,只需要继承上面的单例类。但问题是Singleton::getInstance创建出来的是Singleton对象而不是工厂对象,只是“半个”工厂,怎么办?
这里就必须用CRTP思想了,《Modern C++ code snippets》提到过。就是把子类当做父类的模板参数,这里好让子类创建出来的对象类型是子类。首先要改造Singleton, 给它加一个模板参数:
template<typename T>
class Singleton {
public:
static Singleton& getInstance() {
static T instance; // Local static variable for thread safety
}
// Prevent copying and moving to ensure a single instance
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
Singleton(Singleton&&) = delete;
Singleton& operator=(Singleton&&) = delete;
protected:
Singleton() = default; // Private constructor for singleton
virtual ~Singleton() = default;
};
去掉ProductFactory::getInstance, 继承Singleton<ProductFactory>
class ProductFactory:public Singleton<ProductFactory> {
public:
template <typename T, typename... Args>
//typename std::enable_if<std::is_same<Product,T>::value, void>::type
std::unique_ptr<T> createProduct(Args&&... args) {
return std::make_unique<T>(std::forward<Args>(args)...);
}
private:
ProductFactory() = default; // Private constructor for singleton
friend class Singleton<ProductFactory>; //方便父类getInstance调用上一行的private构造函数
};
好像自己继承了自己似的,这也是CRTP中CR的意思(奇异递归)。如果想了解CRTP请自行查阅资料。
#include <iostream>
#include <memory>
#include <utility>
class Product {
public:
virtual ~Product() = default;
virtual void use() = 0;
};
// Concrete products with different arguments
class Product1 : public Product {
public:
Product1(int arg1) : arg1_(arg1) {}
void use() override { std::cout << "Using Product1 with arg1: " << arg1_ << std::endl; }
private:
int arg1_;
};
class Product2 : public Product {
public:
Product2(int arg2_1, std::string arg2_2) : arg2_1_(arg2_1), arg2_2_(arg2_2) {}
void use() override { std::cout << "Using Product2 with arg2_1: " << arg2_1_ << ", arg2_2: " << arg2_2_ << std::endl; }
private:
int arg2_1_;
std::string arg2_2_;
};
template<typename T>
class Singleton{
public:
static T& getInstance()
{
static T inst; //make sure this parent class can access T's private constructor function by making friends.
return inst;
}
Singleton(T&&) = delete;
Singleton(const T&) = delete;
void operator= (const T&) = delete;
void operator= (const T&&) = delete;
protected:
Singleton() = default;
virtual ~Singleton() = default;
};
class ProductFactory:public Singleton<ProductFactory> {
public:
template <typename T, typename... Args>
//typename std::enable_if<std::is_same<Product,T>::value, void>::type
std::unique_ptr<T> createProduct(Args&&... args) {
return std::make_unique<T>(std::forward<Args>(args)...);
}
private:
ProductFactory() = default; // Private constructor for singleton
friend class Singleton<ProductFactory>;
};
int main() {
ProductFactory& factory = ProductFactory::getInstance();
std::unique_ptr<Product> product1 = factory.createProduct<Product1>(42);
std::string hi("Hello");
std::unique_ptr<Product> product2 = factory.createProduct<Product2>(5, hi);
product1->use();
product2->use();
return 0;
}
$ ./a.out
Using Product1 with arg1: 42
Using Product2 with arg2_1: 5, arg2_2: Hello