一个简单的std :: shared_ptr构造案例的段错误

时间:2014-08-12 02:44:58

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

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;
}

它导致块1 的段错误,但不会导致块2 ,但use_count都是1.我能想到的区别是{ {1}}是基本类型,而int由分配器管理。

我阅读了std::string的{​​{1}},发现此构造函数有一个帖子条件:

bits/shared_ptr.h

问题1:

gcc-4.9是否应该使用另一个智能指针引用的原始指针构造?从这个意义上讲,使用这个构造函数的首选方法如下?

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

问题2:

标准是否明确要求此构造函数,或此帖子条件仅适用于std::shared_ptr

2 个答案:

答案 0 :(得分:8)

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

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

这是双重免费,无效。

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

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

答案 1 :(得分:3)

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

  

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

两个shared_ptr都会尝试删除他们认为现在拥有的对象。我们知道这是未定义的行为,请转到草案C ++标准部分3.7.4.2 解除分配函数,其中说:

  

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

只能通过使用复制构造或复制分配来共享所有权。相同的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())中,因为有两个函数   呼叫永远不会交错。