【问题标题】:Being smart with smart pointers: avoiding shared_ptr overuse使用智能指针变得聪明:避免 shared_ptr 过度使用
【发布时间】:2016-09-17 14:16:31
【问题描述】:

我遇到过类似的代码

bool open_resource(..., shared_ptr<resource> & res)
{
   ...
   shared_ptr<resource> newResource(new resource(...));
   res = move(newResource);
   return true;
}

然后用

调用
shared_ptr<resource> res;
open_resource(..., res);

然后,据我所见, res 不以需要共享指针的方式使用。

当然我马上就想到了改变

   shared_ptr<resource> newResource(new resource(...));
   res = move(newResource);

res = make_shared<resource>(...)

...但后来我遇到了障碍。现在我不能再建议将 shared_ptr 引用更改为更基本的东西了;至少如果我想确保,如果调用者稍后确实需要一个 shared_ptr,则控制块有效地驻留在与对象相同的分配上。为此,它必须从一开始就是一个 shared_ptr。

另一方面,shared_ptr 是一个“重”类型;它有两个计数器和别名以及在大多数呼叫站点中似乎确实不需要的各种功能。然而,如果它是签名中的 shared_ptr,他们必须使用。

我看到的最佳解决方案是将函数体移动到辅助函数,然后重载。

bool get_resource_parameters(Param1& param1,..., ParamN& paramN)
{
   ...
}

bool open_resource(..., shared_ptr<resource> & res)
{
   Param1 param1;
   ...
   ParamN paramN;
   if(!get_resource_parameters(param1,...,paramN))
       return false;

   res = make_shared<resource>(param1,...,paramN);
   return true;
}

bool open_resource(..., unique_ptr<resource> & res)
{
   Param1 param1;
   ...
   ParamN paramN;
   if(!get_resource_parameters(param1,...,paramN))
       return false;

   res = unique_ptr<resource>(new resource(param1,...,paramN));
   return true;
}

但是真的不满意。

有没有人看到更好、更 C++ 的解决方案?

编辑

是的,C++ 方法是返回指针而不是布尔值(并检查空值)。在这种情况下,我无法为 shared_ptr 重载,但我可以将返回的 unique_ptr 临时分配给 shared_ptr 变量,并且适当的构造函数将对其进行转换。

但是,这样我就失去了make_shared的单一分配。我可以保存吗?

【问题讨论】:

  • 为什么不shared_ptr&lt;resource&gt; open_resource(....);
  • 不知道它是否符合您的需求,但使用标准释放器(删除)unique_ptr 与裸ptr 大小相同。 API 中的开销最小且意图明确。
  • @juanchopanza 我知道,我知道,但是返回码/输出参数语法是如此普遍,以至于我不再在这种情况下质疑它。如果它启用了其他方式无法实现的功能,我建议更改它。
  • @ErikAlapää 我知道。我很高兴能够尽可能使用 unique_ptr,但我没有看到一个简单的方法。
  • @FrancescoDondi:不,您不需要两个函数:只需返回一个 unique_ptr - 它可以按需转换为共享函数。

标签: c++ c++11 smart-pointers


【解决方案1】:

std::shared_ptr 有一个converting constructor from std::unique_ptr。为什么不让函数按值返回std::unique_ptr

unique_ptr<resource> open_resource(...);

这也可以作为文档说明这是一个工厂函数,它将resource 的所有权转移给调用者。

让调用者决定他们想要的方式:

auto x = open_resource(...);
// or
std::shared_ptr<resource> x{open_resource(...)};

【讨论】:

  • 是的,它可以,但是您将执行两次分配;第一个用于函数内部的对象,第二个用于函数返回后的共享控制块。我同意如果我只是返回一个 unique_ptr 然后转换它,我可能不会死,但我很好奇,我们能做得更好吗?
  • @FrancescoDondi 您可以使用boost::intrusive_ptr 做得更好,并将引用计数器嵌入到对象中。如果可能的话,我会尽量避免使用智能指针,而支持按值。
  • @FrancescoDondi 如果您要最小化指令和缓存利用率,std::shared_ptr 是最糟糕的选择,因为它的大小和原子指令。
【解决方案2】:

allow unique_ptr/shared_ptr,您可以使用模板:

// Dispatcher for make_unique/make_shared
template <template <typename...> class Ptr, typename T>
struct make_helper;

template <typename T>
struct make_helper<std::unique_ptr, T>
{
    template <typename ...Ts>
    std::unique_ptr<T> operator() (Ts&&... args) const {
        return std::make_unique<T>(std::forward<Ts>(args)...);
    }
};

template <typename T>
struct make_helper<std::shared_ptr, T>
{
    template <typename ...Ts>
    std::shared_ptr<T> operator() (Ts&&... args) const {
        return std::make_shared<T>(std::forward<Ts>(args)...);
    }
};

template <template <typename...> class Ptr, typename T, typename ... Ts>
auto make(Ts&&... args)
{
    return make_helper<Ptr, T>{}(std::forward<Ts>(args)...);
}

然后

bool get_resource_parameters(Param1& param1,..., ParamN& paramN)
{
    //...
}

template <template <typename...> class Ptr>
Ptr<resource> open_resource(...)
{
   Param1 param1;
   ...
   ParamN paramN;
   if(!get_resource_parameters(param1, ..., paramN))
       return nullptr;

   return = make<Ptr, resource>(param1, ..., paramN);
}

并检查 nullptr 而不是 split bool 和 smart_pointer。

【讨论】:

  • 这就是我一直在寻找的那种魔法 :) 我想我从来没有见过(至少没有在很快忘记的抽象示例之外)“模板
  • 不错的解决方案!根据您的需要,您需要考虑 std::unique_ptr 使用自定义删除器的情况。
  • 一个更通用的解决方案可能会接受一个智能指针工厂函数/lambda,因此它不限于这两种智能指针类型。
猜你喜欢
  • 2019-11-19
  • 2020-07-10
  • 2013-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-02
  • 1970-01-01
  • 2012-05-25
相关资源
最近更新 更多