在 *modern* C++ 中,我应该如何管理 *unowned* 指针?

In *modern* C++ how should I manage *unowned* pointers?

modern C++ 中,我应该如何管理 unowned 指针? 我在想 unique_ptrweak_ptr 之类的东西,但它似乎不存在。

例子

例如,如果我有一个 class A 拥有一个指针,我应该使用 unique_ptr<X> 而不是旧的 X* 指针,例如

class A
{
    std::unique_ptr<X> _myX;
};

但是如果我有另一个使用此指针的 class B,我该怎么办?使用 C 风格的指针,我会这样做:

class B
{
    X* _someX;
};

这似乎是正确的,但从代码中看不出我引用了另一个对象的指针(例如 reader 可能认为我可能只是没有使用智能指针)。

我考虑了以下几点

这似乎是一个明显的问题,如果之前有人问过,我们深表歉意。我环顾四周,看到 问题,但不幸的是,OP 在一种特定情况下询问“使用 X* 是否可以”。我正在寻找我通常应该做的事情 而不是 X*,(如果有的话!)。

Bjarne Stroustrup weighed in on this matter.

Pointers are really good at pointing to "things" and T* is a really good notation for that... What pointers are not good at is representing ownership and directly support safe iteration.

他提出两个建议:

  • 考虑 T* 在现代 C++ 中表示“指向 T 的非拥有指针”
  • 如果您希望变量声明明确记录这种非所有权,请使用template<typename T> using observer_ptr = T*;

您的问题主要基于意见,但我们可以看看核心指南对此有何评论:

R.3: A raw pointer (a T*) is non-owning

Reason

There is nothing (in the C++ standard or in most code) to say otherwise and most raw pointers are non-owning. We want owning pointers identified so that we can reliably and efficiently delete the objects pointed to by owning pointers.

准则提倡当原始指针是拥有指针时永远不要使用它。一旦你这样做了,原始指针也很容易表明它是非拥有的。这种解释的缺点是:

Exception

A major class of exception is legacy code, especially code that must remain compilable as C or interface with C and C-style C++ through ABIs. The fact that there are billions of lines of code that violate this rule against owning T*s cannot be ignored. [...]

并非所有原始指针都是非拥有的,而且我们通常对此无能为力。但是,如果您的代码是在 C++11 之后编写的,那么使用原始指针应该足以表明它不是拥有指针。

That seems correct, but it isn't obvious from the code that I've referenced another object's pointer

理想情况下,应该显而易见。如果指针拥有资源,那么它应该是一个智能指针。由于它不是智能指针,这意味着它不应该拥有该资源。

也就是说,在现实中有 C/古老的 C++/设计糟糕的接口使用拥有指针,你可以像这样澄清缺乏所有权:

class B
{
    X* _someX; // no ownership
};

也可以为此目的定义自定义 class 模板包装器,并且已经有一个 proposal 将此类模板包含在标准库中,该模板已被采纳为技术规范, 但还没有被采纳到标准中。据我了解,对于这种包装器是有用还是不必要,还没有达成共识。

https://abseil.io/tips/116 是对此处选项的很好讨论。如果你在建造时有东西,而且永远不需要重新指向它,那么 T& 可能很好。如果你不这样做,那么“嘿,这可能是空的”是该指针现实的一部分,T* 很好——现代 C++ 中的“原始”指针通常传达一个可选的、非拥有的引用。