C ++ shared_ptr 指针访问冲突

C++ shared_ptr<Base> pointer acces violation

我正在使用shared_ptr来创建带有派生类的树列表。但是当我的树被破坏时,我得到了一个指针访问冲突。

我的代码看起来像这样,此外,这实际上类似于我的运行时错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#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<"destruct"<

Base中实现一个名为std::string的字符串,每个实例都不同,我可以看到析构函数被多次调用(因为我认为Base::parent引用)。这当然引发了我的错误,但我仍然不明白为什么会发生这种情况,因为在实际销毁它之前,预计shared_ptr会先对它的引用进行计数!???

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


加上其他人的答案:做你想做的事情的规范方法

1
i->parent = pBase(this);

是使用std::enable_shared_from_this。你

百万千克1

从中派生Base

1
class Base : std::enable_shared_from_this<Base> {

。百万千克1百万千克1

确保每个Base实例都由std::shared_ptr拥有。在您的例子中,这是可以的,因为您在表达式中创建对象

1
pBase child(new Derived());

。百万千克1百万千克1

当你想要一个std::shared_ptr时,使用shared_from_this()而不是this。有问题的一行就会变成

1
i->parent = shared_from_this();

百万千克1


在add()中查看此行

1
i->parent = pBase(this);

每次调用add时,都会创建一个指向this的新共享指针。这些共享指针是独立的——也就是说,它们并不像您想象的那样"共享"。所以,第一次删除子对象时,它的父对象会被删除(因为它是一个共享指针)。因此,您的代码会爆炸。

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

1
Base *parent;


在这里

1
i->parent = pBase(this);

您创建了一个智能指针,它从一个普通的旧指针指向一个对象,而不是直接从新指针获得的对象。不要这样做。

正如@roddy所解释的,您可以使用单独的引用计数器来获得单独的智能指针对象。一个指针的两个引用计数器不起作用。

在您的例子中,按照@roddy的建议,将父对象设置为普通指针可能是可以的。这样,您就不会遇到循环引用的问题。只需确保在删除父指针后永远无法访问父指针。如果您将所有子项和父项一起删除(这将自动发生,除非您将更多智能指针存储到它们的其他位置)

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

1
tree->add(tree, child);

这有点难看,所以您可能要考虑使"添加"成为静态方法,这样就不需要两次传递父级。

另一种选择是:使用另一种智能指针,如boost::invigative_ptr,您可以在该指针中存储引用计数。这样,您就可以找到引用计数,即使您只有一个类似"this"的愚蠢指针。

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