C ++ shared_ptr <base />指针访问冲突

时间:2013-01-16 19:17:13

标签: c++ pointers virtual shared-ptr base

我将shared_ptr<Base>用于某种带有派生类的树列表。但是当我的树被破坏时,我得到了一个指针访问冲突。

我的代码看起来像这样,此外,这实际上模拟了我的运行时错误:

#include <iostream>
#include <memory>
#include <vector>


class Base;
typedef std::shared_ptr<Base> pBase;
class Derived;

class Base {
public:        
    std::vector<pBase> children;

    pBase parent;

    Base() {}
    virtual ~Base() {}

    virtual void doSomething() {}
    void add(pBase i);
};

class Derived : public Base {
    void doSomething() {
        // Do something...
    }
};

void Base::add(pBase i) {
    i->parent = pBase(this);
    children.push_back(i);
}


int main() {
    pBase tree = pBase(new Derived());
    pBase child(new Derived());
    child->add(pBase(new Derived()));
    tree->add(child);
}

当我向Base::~Base添加以下行时:     std :: cout&lt;&lt; &#34;破坏&#34; &LT;&LT;名称&lt;&lt;的std :: ENDL;

Base中实现一个名为name的std :: string,对于每个实例都是不同的,我可以看到析构函数被多次调用(因为我认为Base::parent引用)。那个问题引发了我的错误,但是我仍然不明白为什么会发生这种错误,因为shared_ptr<Base>在实际销毁它之前需要计算它的引用!!?

我希望有人可以告诉我我做错了什么! 但更重要的是,我如何解决这个问题!

3 个答案:

答案 0 :(得分:3)

在add()

中查看此行
i->parent = pBase(this);

每次调用add时,您都会创建一个指向this new 共享指针。这些共享指针是分开的 - 也就是说,它们是 NOT '共享',如你所想。因此,第一次删除子项时,它的父项被删除(因为它是一个共享指针)。因此你的代码就会爆炸。

尝试(作为开始)使父母成为一个普通的哑指针。

Base *parent;

答案 1 :(得分:3)

只是为了增加其他人的答案:在行中做你想做的事情的规范方式

i->parent = pBase(this);

是使用std::enable_shared_from_this。你

  1. 从中获取Base

    class Base : std::enable_shared_from_this<Base> {
    
  2. 确保每个Base实例归std::shared_ptr所有。在您的情况下,这是可以的,因为您在

    这样的表达式中创建对象
    pBase child(new Derived());
    
  3. 如果需要shared_from_this(),请使用this代替std::shared_ptr。有问题的行将成为

    i->parent = shared_from_this();
    

答案 2 :(得分:2)

下面

i->parent = pBase(this);

你创建一个智能指针,从一个普通的旧指针指向一个你没有直接从new获得的对象。永远不要这样做。

正如@Roddy解释的那样,你会得到单独的智能指针对象,带有单独的引用计数器。一个指针的两个引用计数器将不起作用。

在你的情况下,正如@Roddy所提议的那样,使父对象成为正常指针可能没问题。这样,您就不会遇到循环引用问题。确保在删除父项后永远不会访问父指针。如果你将所有孩子与父母一起删除没有问题(这会自动发生,除非你在其他地方存储更多智能指针)

如果要初始化智能指针,基本上有两种选择:在每个界面中使用智能指针。不幸的是,这对“this”不起作用,因为这是一个隐含的参数。您需要在一个额外的参数中手动将已创建的智能指针传递给方法。像这样:

tree->add(tree, child);

这有点难看,所以你可能想考虑“添加”一个静态方法,所以你不需要传递父母两次。

另一种选择:使用另一种智能指针,如boost :: intrusive_ptr,您可以在其中存储引用计数。通过这种方式,您可以找到引用计数,即使您只有像“this”这样的哑指针。

编辑:以下@jpalecek的答案更好。使用那个。塞巴斯蒂安。