当单例类需要支持共享时遇到的问题以及解决方案

引子

        下面给出一个单例模板共享实现:

template <typename T>
class MySingletonRef {
public:
    // new 创建
    static T& GetInstance() {
        auto ret = s_instance.load();
        if (ret == nullptr) {
            std::lock_guard<std::mutex> _l(s_mutex);
            ret = s_instance.load();
            if (ret == nullptr) {
                ret = new T();
                s_instance.store(ret);
            }
        }

        return *ret;
    }
     
    // 共享创建
    static std::shared_ptr<T>& GetSharedInstance() {
        std::call_once(singletonFlag, [&] {
            s_singleton_ = std::make_shared<T>();
        });

        return s_singleton_;
    }
    
    // new 释放
    static void ReleaseInstance() {
        auto tmp = s_instance.load();
        if (tmp != nullptr) {
            std::lock_guard<std::mutex> _l(s_mutex);
            tmp = s_instance.load();
            if (tmp != nullptr) {
                delete tmp;
                s_instance.store(nullptr);
            }
        }
    }

protected:
    MySingletonRef() = default;
    virtual ~MySingletonRef() = default;
    MySingletonRef(const MySingletonRef&) = delete;
    MySingletonRef& operator=(const MySingletonRef&) = delete;
}

        下面是项目中客户简单使用的例子:

class LogReporter : public MySingletonRef<LogReporter>, ...{
    public:
        friend class MySingletonRef<LogReporter>;
        void AddLog(Json::Value log);

    private:
        LogReporter();
        ~LogReporter() override;
        LogReporter(const LogReporter&) = delete;
        LogReporter& operator=(const LogReporter&) = delete;
        ...
};

问题

        以上代码中共享创建的获取接口GetSharedInstance()会报编译错误(大意是make_shared法访问T的构造函数,这个报错很好理解,也一目了然:因为std::make_shared不是T的友元),那问题怎么解决呢?

        难道把std::make_shared声明为Widget的friend就能解决问题吗?

          template <typename T, typename ...Args>
          friend std::shared_ptr<T> std::make_shared(Args &&...);

        并不能,由于标准库函数之间的层层嵌套调用,对于T构造函数的调用可能根本不是发生在make_shared中。如果你想寻根究底地找下去,那自然是可以的,但最终你会获得一份移植性低的代码。(事实上,在C++11下和在C++20下这个调用就发生在不同的函数中。)
        

        解决思路往往是比较简单的,如下代码:wrapper语法糖绕过语法规则。

template <typename T>
class MySingletonRef {
public:
    ...
    // 共享创建
    static std::shared_ptr<T>& GetSharedInstance() {
        std::call_once(singletonFlag, [&] {
            struct make_shared_helper : public T {
                make_shared_helper() : T() {}
            };

            s_singleton_ = std::make_shared<make_shared_helper>();
        });

        return s_singleton_;
    }
    ...
}

总结

        现状:类MySingletonRef可以访问T的构造,make_shared不能访问T的构造。

        解决:包装类绕过编译器。make_shared_helper继承自T,make_shared_helper定义在了MySingletonRef类里面,所以可以访问MySingletonRef的构造,也就间接的可以访问T的构造;同时因为make_shared_helper的构造是公有的,make_shared可以访问,也就间接的可以访问T的构造。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值