C++ 通过受保护的构造函数和静态成员函数强制使用std::shared_ptr来创建实例和std::make_共享

C++ 通过受保护的构造函数和静态成员函数强制使用std::shared_ptr来创建实例和std::make_共享,c++,std,shared-ptr,make-shared,c++17,C++,Std,Shared Ptr,Make Shared,C++17,我有一些类只能通过std::shared\u ptr使用。这些类的实例不是通过在堆栈上分配来直接使用的,也不是通过new的原始指针来使用的。我目前通过使构造函数受保护,并具有一个静态成员函数来实现这一点,该函数实际执行对象实例化,并向对象返回一个共享的ptr: class Example { protected: Example() { } public: static std:shared_ptr<Example> create() { return std::sh

我有一些类只能通过
std::shared\u ptr
使用。这些类的实例不是通过在堆栈上分配来直接使用的,也不是通过
new
的原始指针来使用的。我目前通过使构造函数
受保护
,并具有一个
静态
成员函数来实现这一点,该函数实际执行对象实例化,并向对象返回一个
共享的ptr

class Example {
protected:
    Example() { }
public:
    static std:shared_ptr<Example> create() { return std::shared_ptr<Example>(new Example()); }
};
类示例{
受保护的:
示例(){}
公众:
静态std:shared_ptr create(){return std::shared_ptr(新示例());}
};
我意识到这不是防弹的,因为您仍然可以在
shared\u ptr
上调用
get()

但是,我不能使用
std::make_shared()
,因为构造函数受
保护
,我知道
make_shared()
有内存分配/性能优势


是否存在上述不良做法,或者是否有一种方法可以使用
make_shared()
,而不将构造函数
公开

有一个老把戏,即对另一个函数授予有限的权限来生成对象;你传递一个令牌

struct Example_shared_only {
private:
  // permission token.  explicit constructor ensures
  // you have to name the type before you can create one,
  // and only Example_shared_only members and friends can
  // name it:
  struct permission_token_t {
    explicit permission_token_t(int) {}
  };
public:
  // public ctor that requires special permission:
  Example_shared_only( permission_token_t ) {}
  // delete special member functions:
  Example_shared_only()=delete;
  Example_shared_only(Example_shared_only const&)=delete;
  Example_shared_only(Example_shared_only &&)=delete;
  Example_shared_only& operator=(Example_shared_only const&)=delete;
  Example_shared_only& operator=(Example_shared_only &&)=delete;
  // factory function:
  static std::shared_ptr<Example_shared_only>
  make_shared() {
    return std::make_shared<Example_shared_only>( permission_token_t(0) );
  }
};

这需要保证省略

magic_factory
可以被强制转换到工厂函数生成的任何对象中,并保证省略将该对象构建到位。它在其他上下文中有更丰富的用途,但在这里它允许您导出构造函数以进行共享

传递给
magic\u工厂的lambda是
Example2
的隐式朋友,它可以访问私有ctor。保证省略意味着可以调用具有签名
()->T
的函数来创建
T
“就地”,而无需任何逻辑副本

make_shared
尝试使用参数构造其
T
。当发生这种情况时,C++检查代码>操作符T <代码>;我们的
magic_工厂
有一个这样的
操作员T
。所以它被使用了

它有点像

::new( (void*)ptr_to_storage ) Example2( magic_factory{ lambda_code } )
(如果您不熟悉,这称为“placement new”--它表示“请在
ptr\u to\u storage
指向的位置构建一个
Example2
对象”)


保证省略的美妙之处主要体现在
lambda_code
创建
Example2
的地址中(aka
ptr_to_存储
),对象就在那里构造。

这是一个有问题的设计。为什么类的设计方式会迫使用户通过某些特定的工具来分配它?你能解释一下为什么这些类的
实例没有设计成通过在堆栈上分配它们或通过新的
由原始指针直接使用吗这将有助于为您试图实现/解决的问题提供解决方案。@remy这是一个令人遗憾的C++11答案,C++17从此改变了最佳实践。
::new( (void*)ptr_to_storage ) Example2( magic_factory{ lambda_code } )