【问题标题】:Enforcing std::shared_ptr usage via protected constructors and a static member function to create instances and std::make_shared [duplicate]通过受保护的构造函数和静态成员函数强制使用 std::shared_ptr 以创建实例和 std::make_shared [重复]
【发布时间】:2021-02-09 02:09:09
【问题描述】:

我有一些只能通过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()); }
};

我知道这不是万无一失的,因为您仍然可以在 shared_ptr 上致电 get(),但作为支持使用的指示似乎是合理的。

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

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

【问题讨论】:

  • 这是有问题的设计。为什么类的设计方式迫使它的用户通过一些特定的设施来分配它?
  • 你能解释一下为什么Instances of these classes are not designed to be used directly by allocating them on the stack or by raw pointers via new吗?这可能有助于为您尝试实现/解决的问题提供解决方案。
  • @remy 遗憾地是 C++11 的答案,而 C++17 从那时起改变了最佳实践。

标签: c++ std shared-ptr make-shared


【解决方案1】:

有一个老技巧是给另一个函数有限的权限来创建一个对象;你传递一个令牌。

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。保证省略意味着可以调用具有签名()-&gt;T 的函数来“就地”创建T,而无需任何逻辑副本。

make_shared&lt;T&gt; 尝试使用参数构造其T。发生这种情况时,C++ 会检查 operator T;我们的magic_factory 有一个这样的operator T。所以就用了。

它做了类似的事情

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

(如果您不熟悉,这称为“新放置”——它声明“请在ptr_to_storage 指向的位置构建一个Example2 对象)。

保证省略的美基本上传递到lambda_codeExample2 被创建的地址(又名ptr_to_storage),对象就在那里构造。

【讨论】:

    猜你喜欢
    • 2012-10-06
    • 1970-01-01
    • 2016-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-21
    • 2012-11-03
    相关资源
    最近更新 更多