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

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,可能会造成问题。这个问题我们以后再细细研究。此处先认为加锁更好。

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, 给它加一个模板参数:

#include <mutex>

template<typename T>
class Singleton {
public:
    static Singleton& getInstance() {
        std::lock_guard<std::mutex> lock(mutex); // Acquire lock for thread safety
        static T 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
    virtual ~Singleton()        = default;

private:
    static std::mutex mutex; // Mutex for thread-safe initialization
};

// Initialize the static mutex member outside the class
std::mutex Singleton<T>::mutex;

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>
#include <mutex>

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()
    {
        std::lock_guard<std::mutex> lock(mutex); // Acquire lock for thread safety
        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;

private:
    static std::mutex mutex; // Mutex for thread-safe initialization
};

template<typename T>
std::mutex Singleton<T>::mutex;

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

相关推荐

  1. AI合作 -- 一个modern c++工厂2

    2024-01-09 04:18:03       45 阅读
  2. AI合作 -- 工厂2遗留的问题:bard的错误

    2024-01-09 04:18:03       32 阅读
  3. 工厂模式模式

    2024-01-09 04:18:03       10 阅读
  4. 设计模式(2

    2024-01-09 04:18:03       10 阅读
  5. 设计原则、工厂模式

    2024-01-09 04:18:03       20 阅读
  6. 【Python】模式和工厂模式

    2024-01-09 04:18:03       8 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-01-09 04:18:03       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-01-09 04:18:03       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-01-09 04:18:03       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-01-09 04:18:03       18 阅读

热门阅读

  1. 检查unity打包IOS包含dlopen的块

    2024-01-09 04:18:03       29 阅读
  2. 面试经典150题(72-77)

    2024-01-09 04:18:03       34 阅读
  3. React Hooks之useState、useRef

    2024-01-09 04:18:03       50 阅读
  4. Mysql 中的常用命令

    2024-01-09 04:18:03       33 阅读
  5. 了解一下InternLM2

    2024-01-09 04:18:03       36 阅读
  6. linux 设备模型之类

    2024-01-09 04:18:03       29 阅读
  7. 复杂度分析-时间复杂度和空间复杂度

    2024-01-09 04:18:03       32 阅读
  8. mysql 通过 binglog 恢复数据

    2024-01-09 04:18:03       30 阅读