c++ - 为什么 unique_ptr 和 shared_ptr 不会使构造它们的指针无效?

标签 c++ shared-ptr api-design unique-ptr rvalue-reference

注意:这是一个 API设计问题 ,依靠 unique_ptr 的构造函数的设计和 share_ptr为了这个问题,但不打算对他们当前的规范提出任何改变。

虽然通常建议使用 make_uniquemake_shared , 两个 unique_ptrshared_ptr可以从原始指针构造。

两者都按值获取指针并复制它。两者都允许(即从某种意义上说:不阻止)继续使用在构造函数中传递给它们的原始指针。

以下代码编译并产生双重释放:

int* ptr = new int(9);
std::unique_ptr<int> p { ptr };
// we forgot that ptr is already being managed
delete ptr;

两个unique_ptrshared_ptr如果他们的相关构造函数期望将原始指针作为右值获取,则可以防止上述情况,例如对于 unique_ptr:
template<typename T>
class unique_ptr {
  T* ptr;
public:
  unique_ptr(T*&& p) : ptr{p} {
    p = nullptr; // invalidate the original pointer passed
  }
  // ...

因此,原始代码不会编译,因为左值不能绑定(bind)到右值,而是使用 std::move代码编译,同时更详细和更安全:
int* ptr = new int(9);
std::unique_ptr<int> p { std::move(ptr) };
if (!ptr) {
  // we are here, since ptr was invalidated
}

很明显,用户可以使用智能指针解决许多其他错误。你常用的说法应该知道如何正确使用语言提供的工具,C++不是为了监视你等等。

但是,似乎有一个选项可以防止这个简单的错误并鼓励使用make_shared。和 make_unique .甚至在 make_unique 之前在 C++14 中添加,仍然总是可以选择直接分配而无需指针变量,如:
auto ptr = std::unique_ptr<int>(new int(7));

似乎请求对指针的右值引用作为构造函数参数可以增加一点额外的安全性。此外,当我们获得传递的指针的所有权时,获取右值的语义似乎更准确。

来到 的问题为什么标准不采用这种更安全的方法?

一个可能的原因可能是上面建议的方法会阻止创建 unique_ptr。来自 常量指针 ,即以下代码将无法使用建议的方法进行编译:
int* const ptr = new int(9);
auto p = std::unique_ptr { std::move(ptr) }; // cannot bind `const rvalue` to `rvalue`

但我相信,这似乎是一个值得忽视的罕见情况。

或者,如果需要支持从 const 指针进行初始化是反对所提出方法的有力论据,那么仍然可以通过以下方式实现更小的步骤:
unique_ptr(T* const&& p) : ptr{p} {
    // ...without invalidating p, but still better semantics?
}

最佳答案

只要他们都有 get()方法,使原始指针无效不是“更安全”,而是更令人困惑的方法。

在 C++ 中使用原始指针的方式中,它们本身不代表任何所有权概念,也不定义对象生命周期。任何在 C++ 中使用原始指针的人都需要知道它们仅在对象存在时才有效(无论对象生命周期是由智能指针还是由程序逻辑强制执行)。从原始指针创建智能指针不会“转移”对象的所有权,而是“分配”它。

在以下示例中可以清楚地说明这种区别:

std::unique_ptr<int> ptr1 = std::make_unique<int>(1);
int* ptr2 = ptr1.get();
// ...
// somewhere later:
std::unique_ptr<int> ptr3(ptr2);
// or std::unique_ptr<int> ptr3(std::move(ptr2));

在这种情况下,int 的所有权对象未转移到 ptr3 . ptr1 错误地将其分配给它而没有释放所有权.程序员需要知道指针传递到 std::unique_ptr 的位置。构造函数来自,以及到目前为止如何强制执行对象的所有权。库保证它将使这个特定的指针变量无效可能会给程序员一种错误的安全感,但不会提供任何真正的安全性。

关于c++ - 为什么 unique_ptr 和 shared_ptr 不会使构造它们的指针无效?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60535023/

相关文章:

java - 为什么我的计时器任务不能用于Discord API包装器?

json - 修补多个资源

英特尔 12.1.3 的 c++ shared_ptr 错误

c++ - 如果您始终将其存储在 shared_ptr 中,您的接口(interface)是否需要虚拟析构函数?

c++ - 为什么 shared_ptr 删除器必须是 CopyConstructible 的?

c# - 将方法引入通用基类中是一项重大变化

java - JNI 返回 Java 对象,返回本地引用可以吗,还是必须是全局的?

c++ - 使用 CPR 从 C++ 调用 HTTP?

c++ - 数组是一种数据类型吗?

c++ - 通过索引获取 C++ std::set 的成员