【问题标题】:what if I delete the pointer that the smart pointer is managing?如果我删除智能指针正在管理的指针怎么办?
【发布时间】:2015-05-18 03:20:42
【问题描述】:

我从 C++ Primer 5 Edition 中读取了智能指针。在第 12.1.3 节中,一些描述类似于

智能指针类型定义了一个名为“get”的函数,它返回一个指向智能指针所管理对象的内置指针。该函数适用于我们需要将内置指针传递给无法使用智能指针的代码的情况。 使用“get”返回的代码不得“删除”该指针。

描述下面也给出了一个例子

shared_ptr<int> p(new int(42));       // reference count is 1
int *q = p.get();
{//new block
//undefined:two independent shared_ptr point to the same memory
      shared_ptr<int>(q);
}//block ends, q is destroyed ,and the memory to which q points is freed
int foo = *q; //undefined; the memory to which p points was freed

我可以清楚地理解上面的解释。 但是,当我遇到 Exercise 12.13 时,我有点困惑。

#include<iostream>
#include<memory>
using namespace std;

int main(){
        auto sp = make_shared<int>();
        auto p = sp.get();
        delete p;
        cout <<"Before main function exits!"<<endl;  //which I add for debug
        return 0;
}

编译没有错误。但是运行时出现如下错误

***Error in './ex12_13': double free or corruption(out): 0x09b97018***

以及要调试的上下文 在主函数退出之前! 尚未执行,这意味着错误发生在delete之后 操作在我看来。我也使用 'gdb' 调试这个程序,确实在 delete 之后弹出错误。

那么,如何解释错误? Delete 已经释放了内存,但第二次释放何时发生?在 main 函数退出之前?


我将 sp 的初始化从 make_shared 函数更改为 new 和 使用我自己的 deleter 函数代替 delete

        #include<iostream>
        #include<memory>
        using namespace std;
        int main(){
        auto deleter = [](int*p){
             cout<<"deleter called"<<endl; delete p;
        };
        shared_ptr<int> sp(new int, deleter);
        auto p = sp.get();
        delete p;
        cout <<"Before main function exits!"<<endl;  //which I add for debug
        return 0;
}

然后输出结果是

Before main function exits!
deleter called
***Error in './ex12_13_2': double free or corruption(out): 0x08995998***

当程序超出主作用域时,局部变量shared_ptr p将被销毁,p指向的内存将被调用deleter。

这就是为什么“before main function exits!”首先显示,deleter called稍后显示,最后出现double free错误。

所以我认为我上面提出的困惑主要是来自make_shared。 @Ben Voigt 给出了详细的解释。

您正在删除一个不是来自 new 的指针,因此您有未定义的行为(任何事情都可能发生)。

但深层原因可能只能从make_shared的实现中找到。

【问题讨论】:

标签: c++ pointers shared-ptr smart-pointers


【解决方案1】:

第二次空闲是什么时候在主函数退出之前发生的?

其实不然。现在,您的程序完全有可能在delete p; 上崩溃,但不是因为双重释放。您正在删除不是来自new 的指针,因此您有未定义的行为(任何事情都可能发生)。特别是,make_shared 通常通过将元数据(带有引用计数和删除器)和对象放在单个分配中来优化以最小化分配。可以使用new[] 进行分配,或者生成的对象指针可能位于分配的中间,而不是开始。在任何一种情况下,尝试直接delete 对象(即使释放智能指针以避免未来的第二次释放)只会是麻烦。

但是,我们无法确定崩溃的确切位置,因为您的测试代码有一个小缺陷。

cout <<"Before main function exits!"<<endl;

不会立即显示文本,它只是将其添加到stdout 缓冲区。由于程序崩溃,缓冲区实际上从未写入关联的文件描述符。

对于“printf 调试”,始终使用无缓冲的流,例如 cerr

cerr << "Before main function exits!" << endl;

然后您将看到事件的真实顺序。

【讨论】:

  • flush 操纵器在流上调用flush()endl 也会这样做(在插入换行符之后)。
  • @BenjaminLindley:也许在 iostreams 层之外进行缓冲,然后呢? IDE 可能使用管道来捕获输出,并且也可以缓冲?无论如何,使用stderr 代替stdout 可以避免任何缓冲问题。
  • @BenjaminLindley:我刚刚进行了更改,但执行时没有任何更改。
  • 我很确定你的最后一段就是答案,应该放在首位,而不是事后才想到。
猜你喜欢
  • 2015-02-03
  • 2016-10-09
  • 1970-01-01
  • 2012-09-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-02
  • 1970-01-01
相关资源
最近更新 更多