【问题标题】:Private constructor and make_shared私有构造函数和 make_shared
【发布时间】:2017-07-16 09:38:32
【问题描述】:

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

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 不能?

【问题讨论】:

  • 这段代码没有意义,因为当您仍然为自己保留shared_ptr 的实例(我假设_instanceshared_ptr&lt; MyClass &gt;)并检查是否_instance 时,共享单例的所有权是没有意义的is nullptr 是没有意义的,因为这个块受一次标志保护。在这种情况下让make_shared 工作(例如通过声明它为朋友)也没有任何意义,因为这样就可以在任何地方创建MyClass 的实例。
  • 好的,我明白你的意思,条件if (_instance == nullptr) 不是必需的。 _instance 是一个静态类变量。我不明白为什么shared_ptr 没有意义?

标签: c++ shared-ptr


【解决方案1】:
  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)
{

}

【讨论】:

  • 如果我从没有解决实际问题的事情中抽象出来,剩下的就是make_shared 调用的机制不保留调用者访问私有成员(这里是默认构造函数)的特权make_shared 有;相比之下,new myClass() 中的构造函数调用是在调用者本身的上下文中完成的(这里是 myClass::getInstance 中的 lambda),而不是在 new 的实现中,因此在这种情况下保留了特权。换句话说,make_shared 转发并不完美:它保留了右值/左值的区别,但没有特权。
【解决方案2】:

请认真对待VTT's comment


你的问题:

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

new 实际上是在 member-function 的 lambda 中使用的;嗯,一个 lambda 定义了一个 local-class,而 C++ 标准 permits 一个 local-class 在一个 member-function 中访问所有member-function 可以访问。记住成员函数可以访问私有函数是微不足道的..

std::make_shared 无权访问MyClassprivate。不在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;
}

【讨论】:

    【解决方案3】:

    您应该改用Meyers singleton。您应该确保您的编译器之前支持 C++11 魔术静态。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-08
      • 1970-01-01
      • 1970-01-01
      • 2011-04-20
      • 2011-10-30
      • 2017-03-28
      相关资源
      最近更新 更多