12

如果可以的话,我会*从我的代码中删除所有原始指针,因为使用它们可能不是线程安全的,并且设计的意图也不清楚(可选值、所有权等)。然而,有时不使用指针并不容易。例如,我们倾向于在多态类型的容器中使用基类型的指针:

class A : noncopyable { ... };
class B : public A { ... };

std::vector<A*> v;
v.emplace_back(new B);

// temporary container for some operation
std::vector<A*> selected;
if(check())
   selected.emplace_back(v.front());

你对上面的代码有什么想说的吗?所有者是谁?是不是共享所有权?这就是为什么我们应该这样做的原因v

std::vector<std::unique_ptr<A>> v;
v.emplace_back(make_unique<B>());

现在很明显,v拥有这些对象,但我仍然不喜欢它selected有一个原始指针,这使我的设计不直观。查看标准 C++ 库,我认为只有一种类型可以完成这项工作 - std::reference_wrapper

std::vector<std::unique_ptr<A>> v;
v.emplace_back(make_unique<B>());

// temporary container for some operation
std::vector<std::reference_wrapper<A>> selected;
if(check())
  selected.emplace_back(*v.front());

你觉得这段代码怎么样?这是一个好习惯吗?我知道这一点std::ref()以及std::cref主要使用模板的地方,但似乎在这里我们也可以使用它来清楚地说明我们的设计意图。我看到的唯一问题是我必须取消引用std::reference_wrapperget()并且没有operator*()operator->()内部有相同的接口,就像在容器中一样unique_ptr。我应该自己写类似的东西吗?或者也许可以在未来的 C++ 版本中为这种用例扩展 reference_wrapper?请分享您的反馈。

编辑:我更改了代码示例以更好地显示意图。

4

2 回答 2

6

您已经提供了一个看起来不错的解决方案。我知道问题是“你感觉如何?”

我个人的感觉是,一方面需要在安全性和明确性与另一方面代码的简单性之间存在某种平衡。看起来您的解决方案可能将其推向安全性并过多地损害了简单性。每当我使用包含“弱引用”的容器时,我都会使用原始指针来表示这些。诚然,这可能会让人不太清楚对象的所有者是谁,但它也有一些优点:您不必研究什么是“reference_wrapper”,并且代码很清晰。如果您只是暂时使用它们(弱引用的容器)并且封装了这种用法,那么所有权问题应该是最小的。

但这只是个人喜好的问题,我猜。让我建议为同一目的使用不同的类型。前提是您有能力使用 Boost。对于“强”引用(拥有资源),您可以使用 Steve Watanabe 的Type Erasure库。它不需要显式使用自由存储内存,我想对于小型类型它可以完全摆脱使用堆内存(使用小缓冲区优化)。它最近已被 Boost 接受,尽管我认为尚未发布。

对于弱引用,考虑在 Boost.Optional 中使用“可选引用”:

int i = 0;
boost::optional<int&> oi = i; // note: int&
i = 2;
assert(*oi == 2);

它与reference_wrapper 具有相同的语义。

于 2012-12-14T09:36:55.613 回答
1

我认为称它们为shared_ptr's 在逻辑上没有错误。但是,查看std::weak_ptr的定义:

std::weak_ptr 是一个智能指针,它持有对 std::shared_ptr 管理的对象的非拥有(“弱”)引用。必须将其转换为 std::shared_ptr 才能访问引用的对象。

它可能是一个更好的候选人。至少当您通过指针摆弄时,selected您需要承担临时所有权。由于原始指针存储在共享指针中,因此使用弱指针会更安全。

于 2012-12-14T08:21:06.977 回答