【问题标题】:segment fault for a simple std::shared_ptr construction case简单 std::shared_ptr 构造案例的段错误
【发布时间】:2014-10-04 23:54:52
【问题描述】:

cppreference得知std::shared_ptr有一个构造函数:

template< class Y > explicit shared_ptr( Y* ptr );

我试了一段代码如下:

#include <string>
#include <memory>
#include <iostream>

int main(void) {
  /// block 1
  {
    std::shared_ptr<std::string> s1(new std::string("good"));
    std::shared_ptr<std::string> s2(s1.get()); /// s2
    std::cerr << s2.use_count() << std::endl;
  }
  /// block 2
  {
    std::shared_ptr<int> i1(new int(1));
    std::shared_ptr<int> i2(i1.get());  ///  i2
    std::cerr << i2.use_count() << std::endl;
  }
  return 0;
}

它会导致 block 1 的段错误,但不会导致 block 2 的段错误,但 use_count 都是 1。我能想到的区别是 int是原始类型,而std::string 由分配器管理。

看了gcc-4.9bits/shared_ptr.h,发现这个构造函数有一个后置条件:

use_count() == 1 && get() == __p

问题一:

std::shared_ptr 不应该使用已被另一个智能指针引用的原始指针构造吗?从这个意义上说,使用此构造函数的首选方法如下?

std::shared_ptr<T>(new T(...));

问题2:

标准是否对此构造函数有明确要求,或者此后置条件仅适用于libstdc++

【问题讨论】:

  • 构造函数无法知道原始指针是否来自std::shared_ptr,因此它拥有所有这些。这在这两种情况下都会导致双重删除,因为两个智能指针拥有同一个指针的所有权。

标签: c++ c++11 memory-management shared-ptr smart-pointers


【解决方案1】:

这两种情况都是std::shared_ptr的无效使用。

您不能将相同的原始指针传递给 两个 std::shared_ptr 构造函数并期望得到明确定义的结果。 两个std::shared_ptrs 都会相信他们拥有该指针,并会在超出范围时尝试将其删除。

这是双重免费,无效。

如果你想要两个std::shared_ptrs 来管理同一个对象,你可以用原始指针构造其中一个(或者,更好的是,使用std::make_shared),然后复制构造/分配第二个从第一个。这样,只有当std::shared_ptrs 中的 last 超出范围时,才会释放内存(并触发对象的析构函数)。

在第一种情况而不是第二种情况下出现分段错误的原因可能是因为int 是一种普通类型,因此您没有通过释放指针来运行析构函数int,因为它没有。

【讨论】:

    【解决方案2】:

    这是未定义的行为,我们可以通过 std::shared_ptr constructors 的 cppreference 部分看到这一点(强调我的):

    使用对象的原始指针重载构造 shared_ptr 已经由 shared_ptr 管理的会导致未定义的行为, 即使对象是派生自 std::enable_shared_from_this (换句话说,原始指针重载 承担指向对象的所有权)。

    shared_ptr 都将尝试删除他们认为现在拥有唯一所有权的对象。通过查看 C++ 标准草案 3.7.4.2 Deallocation functions,我们知道这是未定义的行为:

    如果给标准中的释放函数的参数 library 是一个不是空指针值的指针(4.10), 释放函数应释放由 指针,使所有引用的指针的任何部分无效 释放的存储空间。通过无效的指针值间接和 将无效的指针值传递给释放函数有 未定义的行为。 [...]

    所有权只能通过复制构造或复制分配来共享。相同的 cppreference 页面提供了使用复制构造函数的正确示例:

     std::cout << "constructor with object\n";
     std::shared_ptr<Foo> sh2(new Foo);
     std::shared_ptr<Foo> sh3(sh2);     // <- using copy construction
    

    创建shared_ptr 的另一种方法是使用std::make_shared,这样更安全:

    此外,诸如 f(std::shared_ptr(new int(42)), g()) 之类的代码可以 如果 g 抛出异常会导致内存泄漏,因为 g() 可能会被调用 在 new int(42) 之后和 shared_ptr 的构造函数之前。这 不会出现在 f(std::make_shared(42), g()) 中,因为两个函数 呼叫从不交错。

    【讨论】:

    • 对不起,我还没有阅读该说明,很抱歉提出这个问题。
    • @HongxuChen 不用道歉,你没有理解语义,你问了一个问题。你现在有了答案。
    猜你喜欢
    • 2013-08-24
    • 2021-11-29
    • 1970-01-01
    • 1970-01-01
    • 2012-08-13
    • 2016-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多