私有构造函数和make_shared

5

我有一个带有私有构造函数的单例类。在静态工厂方法中,我执行以下操作:

shared_ptr<MyClass> MyClass::GetInstance()
{
    static once_flag onceFlag;

    call_once(onceFlag, []() {
        if (_instance == nullptr)
            _instance.reset(new MyClass());
    });

    return _instance;
}

如果我使用
_instance = make_shared<MyClass>();

代码无法编译。我的问题是:为什么new可以调用私有构造函数而make_shared不能呢?

2
这段代码没有意义,因为在你仍然保留shared_ptr实例(我假设_instanceshared_ptr<MyClass>)的情况下,共享单例的所有权是毫无意义的。检查_instance是否为nullptr也是毫无意义的,因为此块受once标志保护。在这种情况下使make_shared工作(例如通过声明它为friend)也没有意义,因为那样就可以在任何地方创建MyClass实例了。 - user7860670
1
好的,我明白你的意思了,“if (_instance == nullptr)”这个条件是不必要的。_instance是一个静态类变量。但我不明白为什么“shared_ptr”没有意义? - user2683038
可能是重复的问题:如何在只有受保护或私有构造函数的类上调用::std::make_shared?(链接:https://dev59.com/mmsz5IYBdhLWcg3wCDl2) - Toby Speight
3个回答

4
  1. 正如所提到的,std::make_shared 或其组成部分无法访问私有成员。

  2. call_onceonce_flag 是不必要的。它们在 c++11 静态初始化中是隐含的。

  3. 通常情况下,您不会想要公开共享指针。

class MyClass
{
    MyClass() {}

public:
    static MyClass& GetInstance()
    {
        static auto instance = MyClass();
        return instance;
    }
};

然而,我可以想象出一种情况,您可能希望将指向impl的共享指针公开 - 这是在类可以选择“断开”或“重置”impl为新实现的情况下。在这种情况下,我会考虑编写以下代码:

class MyClass2
{
    MyClass2() {};

    static auto& InternalGetInstance()
    {
        static std::shared_ptr<MyClass2> instance { new MyClass2 };
        return instance;
    }

public:

    static std::shared_ptr<MyClass2> GetInstance()
    {
        return std::atomic_load(std::addressof(InternalGetInstance()));
    }

    static void reset() {
        std::atomic_store(std::addressof(InternalGetInstance()),
                        std::shared_ptr<MyClass2>(new MyClass2));

    }  
};

然而,最终我认为类的“静态性”应该是实现细节,对于类的用户来说并不重要。
#include <memory>
#include <utility>

class MyClass
{
    // internal mechanics

    struct Impl {

        auto doSomething() {
            // actual implementation here.
        }
    };

    // getImpl now becomes the customisation point if you wish to change the
    // bahviour of the class later
    static Impl& getImpl() {
        static auto impl = Impl();
        return impl;
    }


    // use value semantics - it makes for more readable and loosely-coupled code
public:
    MyClass() {}

    // public methods defer to internal implementation

    auto doSomething() {
        return getImpl().doSomething();
    }
};


int main() {

    // note: just create objects
    auto mc = MyClass();
    mc.doSomething();

    // now we can pass the singleton as an object. Other functions don't even
    // need to know it's a singlton:

    extern void somethingElse(MyClass mc);
    somethingElse(mc);
}

void somethingElse(MyClass mc)
{

}

{btsdaf} - Marc van Leeuwen

1
请认真对待 VTT的评论

针对您的问题:

我的问题是:为什么new可以调用私有构造函数,但make_shared不行?

new实际上是在一个成员函数中的lambda表达式中使用的;嗯,lambda定义了一个本地类,C++标准允许在成员函数内部的本地类可以访问成员函数可以访问的所有内容。而且很容易记住成员函数可以访问私有成员。

std::make_shared无法访问MyClass的私有成员。它在MyClass的作用域之外。


例子:

class X{
    X(){};
public:
    static X* create(){ // This member-function can access private functions and ctors/dtor
        auto lm = [](){ // This lambda inherits the same access of enclosing member-function
               return new X();
        };
        return lm();
    }
};

int main(){
    auto x = X::create();    //valid;
    auto y = new X();        //invalid;
}

0

你应该使用Meyers singleton。在此之前,你应确保你的编译器支持C++11魔术静态。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接