在c ++ shared_ptr中相互破坏

时间:2018-06-02 12:34:12

标签: c++ c++11

大家。我在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在这种情况下有一些错误。

3 个答案:

答案 0 :(得分:7)

有一个问题在起作用,而另一个问题会在您修复时弹出。

第1部分:不共享的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进行管理。虽然,它确实已经这样做了,所以没关系。

第2部分:循环和循环 - 循环引用计数

现在,一旦你把它放到你的程序中,它就不会崩溃了。它也不会打印任何内容,就好像什么都没有被删除一样。是什么给了什么?

事实上,没有任何东西被删除,原因是就所有shared_ptr而言,它们的对象永远不会被淘汰。我们偶然发现引用计数不是一个完整的垃圾收集器的主要原因。

你的B对象知道10个A个对象,但我只考虑其中一个 - 这就是我要展示10次的事情。他们彼此之间有shared_ptr个,然而有许多外部shared_ptr只要他们需要就会让他们活着。一旦所有这些外部引用都消失了,您希望它们被删除,但请考虑那时的情况:

+-----+   shared_ptr<A>   +-----+
|     | ----------------> |     |
|  b  |                   |  a  |
|     | <---------------- |     |
+-----+   shared_ptr<B>   +-----+

bshared_ptra,因此a的引用次数为1. a的{​​{1}}到{shared_ptr 1}},所以b的引用计数为1.我们陷入了捕获22:b正在使用中,因为a正在使用,b正在使用,因为b正在使用中,因此都不会被删除。

使用a时,绝对必须避免这种设置,这就是shared_ptr存在的原因。我必须猜测你想要所有权机制的方式,但是例如你可以拥有从weak_ptrA对象的非拥有引用,如下所示:

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使用普通指针释放的普通指针。