大家。我在c ++解构中发现了一个问题。我将在下面向您展示我的代码:
logBefore() is running!
classname : org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint,getConnection
******
结果的一部分是
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
class B;
class A{
public:
typedef shared_ptr<A> Ptr;
shared_ptr<B> b;
int index;
A(const shared_ptr<B>& b_, int index_):b(b_), index(index_){}
~A(){cout << "A is deleted." << endl;
cout << "A id is " << index << endl;}
};
class B{
public:
typedef shared_ptr<B> Ptr;
vector<A::Ptr> all_a;
void addA(){
for(int i=0; i<10; i++){
auto a = make_shared<A>(Ptr(this), i);
all_a.push_back(a);
}
}
int index;
B(int index_):index(index_){
}
~B(){
cout << "B is deleted." << endl;
cout << "B id is " << index << endl;
}
};
int main(){
B::Ptr b = make_shared<B>(5);
b->addA();
return 0;
}
我的代码发生了什么,如何调试此问题。 我认为shared_ptr在这种情况下有一些错误。
答案 0 :(得分:7)
有一个问题在起作用,而另一个问题会在您修复时弹出。
shared_ptr
所以,你看到很多析构函数调用同一个B
对象并且出现双重自由错误的原因是这一行:
auto a = make_shared<A>(Ptr(this), i);
特别是,Ptr(this)
从shared_ptr<B>
开始this
独立 - 它无法知道在其他地方还有其他shared_ptr
对其负责对象,它有自己的引用计数,它会相信没有其他人使用它指向的对象,当它变为零。出于所有意图和目的,它的行为类似于unique_ptr
,因为它永远不会与另一个shared_ptr
(临时除外)共享其引用计数。
不幸的是,您不知道在成员函数中管理您正在处理的对象的shared_ptr
。幸运的是,标准库中有一些东西可以帮助解决这个问题:std::enable_shared_from_this
它的工作原理如下:
class B : public std::enable_shared_from_this<B> {
// ...
auto a = make_shared<A>(shared_from_this(), i);
请注意,如果您执行此操作,则代码期望B
仅由shared_ptr
进行管理。虽然,它确实已经这样做了,所以没关系。
现在,一旦你把它放到你的程序中,它就不会崩溃了。它也不会打印任何内容,就好像什么都没有被删除一样。是什么给了什么?
事实上,没有任何东西被删除,原因是就所有shared_ptr
而言,它们的对象永远不会被淘汰。我们偶然发现引用计数不是一个完整的垃圾收集器的主要原因。
你的B
对象知道10个A
个对象,但我只考虑其中一个 - 这就是我要展示10次的事情。他们彼此之间有shared_ptr
个,然而有许多外部shared_ptr
只要他们需要就会让他们活着。一旦所有这些外部引用都消失了,您希望它们被删除,但请考虑那时的情况:
+-----+ shared_ptr<A> +-----+
| | ----------------> | |
| b | | a |
| | <---------------- | |
+-----+ shared_ptr<B> +-----+
b
有shared_ptr
到a
,因此a
的引用次数为1. a
的{{1}}到{shared_ptr
1}},所以b
的引用计数为1.我们陷入了捕获22:b
正在使用中,因为a
正在使用,b
正在使用,因为b
正在使用中,因此都不会被删除。
使用a
时,绝对必须避免这种设置,这就是shared_ptr
存在的原因。我必须猜测你想要所有权机制的方式,但是例如你可以拥有从weak_ptr
到A
对象的非拥有引用,如下所示:
B
和class A{
// ...
weak_ptr<B> b;
:
B
然后void addA(){
for(int i=0; i<10; i++){
auto a = make_shared<A>(weak_from_this(), i);
all_a.push_back(a);
}
}
中的对象将不会保留拥有它们的all_a
对象。要使用它,你会写
B
让std::shared_ptr<B> b2 = b.lock();
在你使用它时保持活着。
答案 1 :(得分:1)
当您执行shared_ptr<B>(this)
两次时,您无法获得成功管理shared_ptr
的2 this
。你得到2 shared_ptr
个独立的控制块,每个控制块都认为他们是this
的唯一管理者。这不可避免地导致双重删除或更糟。
解决此问题的一种方法是使用shared_from_this()
代替this
;例如see here。
答案 2 :(得分:0)
在C ++ reference中明确指出了这一点:
只能通过复制构造或将对象的值分配给另一个shared_ptr来与另一个shared_ptr共享对象的所有权。使用另一个shared_ptr拥有的原始基础指针构造新的shared_ptr会导致未定义的行为。
我记得那句老话:如果一切都失败了,请先阅读说明。
您也不能在代码中混合使用普通指针和shared_ptr。您可能会持有一个已由shared_ptr使用普通指针释放的普通指针。