V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
innoink
V2EX  ›  问与答

想了一晚上想不出一个完美的 c++ singleton 方案

  •  
  •   innoink · 2018-11-01 01:28:31 +08:00 · 2097 次点击
    这是一个创建于 2219 天前的主题,其中的信息可能已经有所发展或是发生改变。

    目前能想到的最完善的方案和这个类似:

    https://stackoverflow.com/questions/24964769/singletons-curiously-recurring-template-pattern-and-forwarding-constructor-para/24966418#24966418

    要求Foo类必须包含一个不带参的构造函数,这样的话,构造完成之后,用到的地方不用再把参数塞进getInstance里。

    和这个答主说的一样,问题在于,一般我们想把Foo做成单例时,如果Foo包含带参数的构造函数,意味着我们想直接通过参数构造一个全局唯一的实例。但是根据上一段话,我们也希望它同时包含不带参的构造函数方便后续调用。

    但这样一来,这个不带参的构造函数的意义并不在于"构造",实际上我们期望不通过它来构造,就显得很不自然。而且可能一上来误用不带参的 ctor 导致这个单例没构造成功。

    我期望的用法是:

    1. Foo包含一个带参构造函数(比如Foo::Foo(int a)),只能通过它来构造;
    2. 如果程序第一次执行的是Foo::getInstance(123),则成功构造,后续应当支持Foo::getInstance()这种简便写法;
    3. 如果程序第一次执行的是Foo::getInstance(),则报错,停止构造

    目前想到的在无参的Foo::Foo()中,直接抛异常。这样能满足以上需求,但是却很不优雅,希望Foo类不动,通过修改singleton来实现。

    想了一晚上了,没想出好办法。

    第 1 条附言  ·  2018-11-01 13:06:59 +08:00

    似乎只能把 初始化 和 获得引用 做成两个函数了。

    #include <stdexcept>
    #include <utility>
    #include <mutex>
    #include <memory>
    
    //thread-safe singleton in c++11
    //use shared ptr, because make_unique is avaliable only after c++14
    template <typename T>
    class singleton
    {
    public:
        template<typename... Args>
        static void init_instance(Args&&... args)
        {
            try {
                std::call_once(init_flag,
                               [](Args&&... args)
                               {
                                   p_instance = std::make_shared<T>(std::forward<Args>(args)...);
                               },
                               args...);
            } catch (...) {
            }
        }
    
        static T& get_instance()
        {
            if (p_instance == nullptr)
                throw std::logic_error("instance not initialized.");
            return *p_instance;
        }
    private:
            singleton(void) = delete;
            virtual ~singleton(void) = delete;
            singleton(const singleton&) = delete;
            singleton& operator = (const singleton&) = delete;
    private:
        static std::shared_ptr<T> p_instance;
        static std::once_flag init_flag;
    };
    
    template <typename T> std::shared_ptr<T> singleton<T>::p_instance;
    template <typename T> std::once_flag singleton<T>::init_flag;
    

    用智能指针是为了在程序结束时正确析构;最好用unique_ptr,但是在c++11里没有make_unique,这里用shared_ptr也没什么错

    3 条回复    2018-11-02 11:12:38 +08:00
    innoink
        1
    innoink  
    OP
       2018-11-01 13:09:33 +08:00
    终于想出一个还过的去的方案
    agagega
        2
    agagega  
       2018-11-01 13:41:09 +08:00
    你不分开,还得用异常来处理,多麻烦
    innoink
        3
    innoink  
    OP
       2018-11-02 11:12:38 +08:00 via Android
    @agagega 额,异常本来也是避免不了的,因为构造函数本身就有可能抛异常
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1069 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 19ms · UTC 21:37 · PVG 05:37 · LAX 13:37 · JFK 16:37
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.