在现代C++中,我应该如何管理*未拥有*的指针?

5
在现代C++中,我该如何管理无主指针?
我认为可以使用类似于unique_ptr的weak_ptr,但是好像并不存在。
例如,如果我有一个拥有指针的类A,我应该使用unique_ptr而不是旧的X*指针。
class A
{
    std::unique_ptr<X> _myX;
};

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

class B
{
    X* _someX;
};

看起来代码没问题,但并不明显我引用了另一个对象的指针(例如一个读者可能会认为我可以不使用智能指针)。

我的考虑如下

  • std::shared_ptr<X> - 由于A保证能够比B生存得更久,因此似乎浪费了引用计数。
  • std::weak_ptr<X> - 只能与shared_ptr一起使用。
  • X& - 仅当X&在B的构造函数中可用时才有效。

这似乎是一个显而易见的问题,如果已经有人问过了,请原谅。我找到了这个问题,但不幸的是OP只问了一个特定情况下是否可以使用X*。我想知道我应该通常做些什么,而不是使用X*(如果有任何其他选择的话!)。


15
一个原始指针本质上意味着你拥有一个非所有权指针,因此没有什么需要添加或做的。换句话说,让类 B 携带一个原始指针,这表明你不能管理该对象的内存。 - Cory Kramer
不是重复,而是相关的历史好奇:https://dev59.com/y1wZ5IYBdhLWcg3wGs7D - Drew Dormann
你会发现关于这个问题有不同的意见,但人们使用的另一个主要选项是为了传达意图而纯粹使用相同事物的不同拼写(例如observer_ptr,尽管有些东西使用别名,有些则使用单独的类型)。 - chris
管理所有权,而不是指针。 - C.M.
4个回答

10
Bjarne Stroustrup已经对这个问题发表了自己的看法。
引用如下:
指针非常擅长指向"事物",而T*是一个非常好的表示方法...指针不擅长表示所有权。
他提出了两个建议:
1. 在现代C++中,考虑将T*解释为"非拥有指针"。 2. 如果您希望您的变量声明能够明确地说明非所有权关系,请使用template using observer_ptr = T*;。

有关于 observer_ptr 的模板类的考虑,而不是将其作为原始指针的别名。因为这会改变类型,从而具有特定的目的。 - ALX23z
这是一个糟糕的 observer_ptr 实现。Experimental 有一个更好的实现:https://en.cppreference.com/w/cpp/experimental/observer_ptr。 - SergeyA
2
@SergeyA “better” 是有争议的 - 这就是 Stroustrup 在文档中花费 3 页时间抨击的同一个 observer_ptr,最后建议使用 Drew 发布的替代版本。 - c z

4

看起来是正确的,但从我引用另一个对象的指针的代码中并不明显

理想情况下,这应该是明显的。如果指针拥有资源,则应该是智能指针。既然它不是智能指针,那就意味着它不应该拥有该资源。

话虽如此,在使用拥有指针的C /古老的C++ /设计不良的接口的现实中,您可以通过以下方式澄清缺乏所有权:

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

还可以定义一个自定义类模板包装器来实现此目的,并且已经有提议将这样的模板纳入标准库,该提议被采纳为技术规范,但尚未纳入标准。据我所知,对于是否需要这样的包装器尚无共识。


1
你的问题基本上是基于个人意见的,但我们可以看一下核心指南对此的建议:
R.3:原始指针(T*)是非拥有的
原因
没有任何东西(在C++标准或大多数代码中)表明其他情况,大多数原始指针都是非拥有的。我们希望确定拥有指针,以便我们可以可靠高效地删除由拥有指针指向的对象。
该指南提倡永远不要在拥有指针时使用原始指针。一旦这样做,原始指针也会很容易地表明它是非拥有的。这种解释的缺点是:
例外情况
一个主要的例外类别是遗留代码,特别是必须保持编译为C或通过ABIs与C和C风格的C++接口的代码。违反这个关于拥有T*的规则的代码行数达数十亿,这个事实不能被忽视。...

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


@eerorika 最后一部分是匆忙写的。在我修复它之前,我会将其删除。 - 463035818_is_not_a_number

1

https://abseil.io/tips/116 是这里选项的良好讨论。如果您在构建时拥有该对象,并且从未需要重新指向它,则 T& 可能很好。如果没有,则“嘿,这可能为空”是该指针的现实部分,T* 就可以了 - 在现代C++中,“原始”指针通常表示可选的、非所有权的引用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接