C++ 又一个",;未分配要释放的指针";

C++ 又一个",;未分配要释放的指针";,c++,c++11,smart-pointers,C++,C++11,Smart Pointers,好的,我知道有很多关于这个错误的帖子,但是我找不到一个简单的例子和对这个问题的清晰解释。免责声明:我发誓这不是一项任务:)我只是在玩弄智能指针,想弄明白它们,结果发生了 shared_ptr<string> a = make_shared<string>("This is a string."); shared_ptr<string> b = make_shared<string>("This is b string."); cout <&l

好的,我知道有很多关于这个错误的帖子,但是我找不到一个简单的例子和对这个问题的清晰解释。免责声明:我发誓这不是一项任务:)我只是在玩弄智能指针,想弄明白它们,结果发生了

shared_ptr<string> a = make_shared<string>("This is a string.");
shared_ptr<string> b = make_shared<string>("This is b string.");
cout << a.use_count() << " " << b.use_count() << endl;
a.reset(b.get());
cout << a.use_count() << " " << b.use_count() << endl;
很公平,我想。毕竟,我传递的是一个原始指针,编译器/运行时应该如何知道其关联的控制块,对吗?正确的?!(好的,我真诚地希望编译器能够解决这个问题,但我认为这是一个简单的例子,如果不发生这种情况,一定有很好的理由)

接下来,我有以下代码

cout << a << " " << *a << endl;
cout << b << " " << *b << endl;
好的,这正是我所期望的:a现在指向b。但是,紧接着,我得到了一个返回0,并且我从main()中退出,而输出会发出以下消息

tests(62775,0x7fff7940d000) malloc: *** error for object 0x7f8f10403288: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
现在我很困惑:到底哪个被释放的指针没有被分配?我的钱会在a或b上,因为它们指向同一个东西,自然会超出范围,因此在同一个指针上会调用它们的delete两次。我说得对吗?我错过什么了吗?还有什么我绝对应该知道的吗

编辑我尝试了等效的哑指针

string* a = new string("This is a string");
string* b = new string("This is b string");

a = b;

cout << *a << endl << *b << endl;
string*a=新字符串(“这是一个字符串”);
字符串*b=新字符串(“这是b字符串”);
a=b;

cout您永远不应该
从智能指针中获取原始指针,并将其传递到另一种智能指针类型,就像您所做的那样(使用
a.reset(b.get());

正在发生的事情: 发生的情况是,智能指针
a
b
的“所有者组”都试图管理相同的原始指针。当您将原始指针传递到
reset
函数时,共享的\u ptr
a
假定它正在获取一个未拥有的指针,因此它将管理原始指针的生存期。删除所有权组中的最后一个共享\u ptr时,将取消分配关联的内存

问题是
b
删除该字符串,然后
a
在销毁该字符串时再次尝试删除该字符串。你不应该尝试两次释放相同的内存。否则,行为是未定义的,您可能会得到一个异常,或者您可能会覆盖不应该覆盖的内存(因为它已被重新分配),这可能会在程序的不相关部分导致各种错误

当然,这发生在return语句之后,因为此时智能指针
a
b
超出范围(在堆栈上)并被破坏

如何将一个共享ptr分配给另一个: 要分配共享指针,只需说:

a = b;
这将自动取消分配
a
指向的任何对象,并将其设置为
b
,但智能指针将知道它们都指向同一对象,而不是试图独立管理同一字符串的生存期


基本上,共享指针的
操作符=
(以及复制构造函数)会创建一个属于同一组(共享同一引用计数)的新共享指针。因此,两个共享指针的
use\u count()
都将返回
2
(因为它们彼此都知道),并且只有在最后一个共享指针被破坏时,内存才会被释放。

函数的
reset
需要指向新分配的对象的指针,而不是已经由另一个共享指针管理的对象。所以你的怀疑是对的
a
b
都认为他们是指针的唯一所有者。因此,当它们超出范围时,它们都在各自的析构函数中对同一指针调用delete。如果希望
a
b
共享所有权,只需使用
shared\u ptr
赋值运算符即可

a = b;

由于
a
b
对该指针的使用计数分别为1,因此
b
所拥有的指针在
a
b
超出范围时被释放两次

你的哑指针等价物根本不能释放任何东西,所以它不会崩溃,但如果你在valgrind下运行它,你会发现它泄漏了


顺便说一句,对于库来说,确实没有任何好的方法来确定指针是否已经被另一个智能指针对象所拥有。(这不是编译器的工作——编译器只是翻译代码。)控制块不一定靠近对象。即使是,如果它不存在,也可能触发分段错误。也许
shared_ptr
可以维护一个拥有指针的全局哈希表,但这样会有太多开销。因此,您必须明智地编写代码。切勿调用
.get()
并将原始指针传递给将拥有所有权的函数。

您需要了解
共享的ptr
使共享的
是库功能,而不是编译器的功能

编译器不检查提供它们所包含指针的上下文-库函数的行为在标准中指定。在实践中,这意味着行为是在库的源代码中硬编码的,编译器不会根据使用的上下文进行更改

get()
成员只返回一个原始指针。它不会导致包含对象以某种方式注册该对象可能由另一个
共享\u ptr
管理

另一方面,
reset()
成员假定它正在接收一个无主指针,并声明所有权

因此,
a.reset(b.get())
使
a
b
都认为它们拥有相同的指针,并对其生命周期负责,而两者都不知道另一个指针声称拥有所有权。当
a
被销毁时,它将删除该指针。
b
也是如此。净效应是删除t
a = b;
a = b;