与AI合作 -- 写一个modern c++单例工厂2

发布时间:2024年01月09日

1. 前言

上节我们完成了一个单例工厂类,单例和工厂合到一起了。本节我们将分别实现它们,便于代码重用。OKay,AI助手上场。

2. 单例单拎出来

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这个问题回答的很好,

2.1 static变量要锁保护吗?

只是有一点有点怀疑:static Singleton instance需要锁保护吗?要回答这个问题,首先必须知道这个变量什么时候初始化?

1.在C++中,全局static变量和class的static成员变量在main函数之前初始化,main函数之后销毁。

2. 函数内部的局部static变量在该函数第一次被调用时初始化,在main函数之后销毁

3.对于static数据即使是函数内部的对象则存放在全局数据区,全局数据区的数据并不会因为函数的退出就将空间释放。

如果两个线程同时调用Singleton::getInstance会同时初始化Singleton instance,可能会造成问题。但是这一点,大牛们早就想到了,这里不必加锁,AI有点画蛇添足了。这个问题我们以后再细细研究。

2.2 函数中的static变量何时初始化?

还有一个问题: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时机晚了。还好没有这个问题。

3. 改造我们的工厂类

有了上面的单例类,我们的工厂类就可以去掉getInstance()函数了,只需要继承上面的单例类。但问题是Singleton::getInstance创建出来的是Singleton对象而不是工厂对象,只是“半个”工厂,怎么办?

3.1 先改造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;
};

3.2 改造工厂类:

去掉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请自行查阅资料。

4. 全部代码

#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

文章来源:https://blog.csdn.net/zhaiminlove/article/details/135462616
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。