通过受保护的构造函数和静态成员函数强制std :: shared_ptr使用以创建实例和std :: make_shared

问题描述

我有一些只能通过std::shared_ptr使用的类。这些类的实例不能设计为通过在堆栈上分配它们或通过new由原始指针直接使用。我目前通过使构造函数protected并具有一个static成员函数来实际执行此操作,该成员函数实际上会进行对象实例化并将shared_ptr返回给对象:

class Example {
protected:
    Example() { }
public:
    static std:shared_ptr<Example> create() { return std::shared_ptr<Example>(new Example()); }
};

我意识到这不是防弹的方法,因为您仍然可以在get()上致电shared_ptr,但这似乎可以作为支持人员使用的一种指示。

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

上面的做法是不好的做法,还是有没有使用make_shared()而不构造构造函数public方法

解决方法

有一个古老的技巧可以将有限的权限授予另一个函数来创建对象;您传递令牌。

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) );
  }
};

现在Example_shared_only::make_shared()返回一个由shared_ptr创建的make_shared,其他人无能为力。

如果您可以使用C ++的更多现代方言,我们可以做得更好:

template<class F>
struct magic_factory {
  F f;
  operator std::invoke_result_t<F const&>() const { return f(); }
};

struct Example2 {
  static std::shared_ptr<Example2> make() {
    return std::make_shared<Example2>( magic_factory{ []{ return Example2{}; } } );
  }
private:
  Example2() = default;
};

Live example

这需要才能保证清除。

magic_factory可以强制转换为工厂函数产生的任何内容,并且可以保证简化地就地构建该对象。它在其他情况下具有更出色的用途,但是在这里,您可以导出构造函数以使其共享。

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

make_shared<T>尝试使用参数构造其T。 C ++在发生这种情况时会检查operator T;我们的magic_factory有一个这样的operator T。因此就使用了。

它的作用类似

::new( (void*)ptr_to_storage ) Example2( magic_factory{ lambda_code } )

(如果您不熟悉,这称为“新放置”-它表示“请在Example2指向的位置上构建ptr_to_storage对象)。

保证清除的本质基本上是将lambda_code的创建地址(也称为Example2)传递到ptr_to_storage,并在此处构造对象。