【问题标题】:C++ shared_ptr<Base> pointer acces violationC++ shared_ptr<Base> 指针访问冲突
【发布时间】:2013-01-16 19:17:13
【问题描述】:

我将shared_ptr&lt;Base&gt; 用于某种带有派生类的树列表。但是当我的树被破坏时,我会遇到指针访问冲突。

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

#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

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

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

【问题讨论】:

  • 您没有初始化tree,还有其他问题...
  • @ybungalobill 很抱歉,我也看不到您指的是什么?
  • 因为你在我指出之后修好了...
  • @ybungalobill 我是说the other problems you have?

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


【解决方案1】:

在add()中看这一行

i->parent = pBase(this);

每次调用 add 时,都会创建一个 new 指向 this 的共享指针。这些共享指针是独立的——也就是说,它们如您所想的那样“共享”。所以,当你第一次删除一个孩子时,它的父母会被删除(因为它是一个共享指针)。因此,您的代码崩溃了。

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

Base *parent;

【讨论】:

  • 谢谢!应该是这样的!
  • 我不建议在一个对象上混合普通指针和智能指针。最后,可能会删除父级,留下一个悬空指针。
【解决方案2】:

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

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. 如果您需要std::shared_ptr,请使用shared_from_this() 而不是this。有问题的行会变成

    i->parent = shared_from_this();
    

【讨论】:

  • 谢谢,这实际上是解决问题的最简单方法!:) 编辑:如果大规模使用,与其他方法相比,这是一种快速的方法吗?
  • @Tim:它应该和 sebastian 的一样快,但空间开销很小。
  • 整洁,看起来有一个 boost 等价物,因为我在没有 C++11 编译器的情况下被困住了。
【解决方案3】:

这里

i->parent = pBase(this);

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

正如@Roddy 所解释的,您会获得单独的智能指针对象和单独的引用计数器。一个指针的两个引用计数器不起作用。

在您的情况下,正如@Roddy 建议的那样,将 parent 设为普通指针可能是可以的。这样,您就不会遇到循环引用的麻烦。只要确保您在删除父级后永远不会访问父级指针。如果您将所有子项与父项一起删除,则没有问题(这会自动发生,除非您在其他地方存储更多指向它们的智能指针)

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

tree->add(tree, child);

这有点难看,因此您可能需要考虑将“添加”作为静态方法,这样您就不需要两次传递父级。

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-10
    • 1970-01-01
    • 2017-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-17
    • 1970-01-01
    相关资源
    最近更新 更多