简体   繁体   English

为什么在使用shared_ptr时出现此运行时异常?

[英]why am i getting this runtime exception when using shared_ptr?

In the following code, I am getting the following runtime exception (possibly memory leak) after return 1; 在下面的代码中,返回1之后,我得到了以下运行时异常(可能是内存泄漏); and in destructor of Node(). 并在Node()的析构函数中。

Unhandled exception at 0x0f9bad4a (msvcp100d.dll) in test.exe: 0xC0000005: Access violation reading location 0xfeeefef2.

It's been a while since I used smart_ptr, so am trying to learn what am I doing wrong here ? 自从我使用smart_ptr以来已经有一段时间了,所以我在尝试了解我在做什么错吗?

#include <vector>
#include <queue>
#include <memory>

#include <iostream>
using namespace std;

class Node;
typedef shared_ptr<Node> SharedNode;

class Node {
    Node* parent;
    vector< SharedNode > children;
    int value;

    //limiting construction
    Node(int a_value):value(a_value),parent(0){}
    Node(const Node &copy); //non-construction-copyable
    Node& operator=(const Node& copy); //non-copyable
public:
    static SharedNode create(int a_value){
        return SharedNode(new Node(a_value));
    }
    SharedNode addChild(SharedNode child){
        child->parent = this;
        children.push_back(child);
        return child;
    }

SharedNode getNode(int searchValue);
};

SharedNode Node::getNode(int searchValue){

    // Breadth First Search
    queue<SharedNode> que;
    que.push(SharedNode(this));

    while(!que.empty()){
        SharedNode node = que.front();
        que.pop();

        if(node->value == searchValue)
            return node;

        vector<SharedNode>::iterator it;
        for(it = node->children.begin(); it != node->children.end(); it++){
            que.push(*it);
        }
    }

    return 0;
}

int main(){
    SharedNode node_ptr = Node::create(5);

    for(int i  = 0; i < 4; ++i)
        node_ptr->addChild(Node::create(i));

    cout << (node_ptr->getNode(-1) != 0 ? "Found" : "Not found");

    return 1;
}

I think I'm messing up when I use shared_ptr on this, like: shared_ptr(this) . 我想在此上使用shared_ptr时会搞砸,例如: shared_ptr(this) But then, that's my guess. 但是那是我的猜测。

What am I doing wrong here ? 我在这里做错了什么?

The problem is from 问题出在

que.push(SharedNode(this));

This creates a new shared pointer that now owns this . 这将创建一个现在拥有this的新共享指针。 However, due to the create() method, there is another shared pointer that owns the same object. 但是,由于create()方法,存在另一个拥有相同对象的共享指针。 This can result in a double delete. 这可能导致双重删除。

If you have a reason to use a shared pointer in this situation, the correct solution is enable_shared_from_this . 如果在这种情况下有理由使用共享指针,则正确的解决方案是enable_shared_from_this

First, change the node definition to this. 首先,将节点定义更改为此。

class Node : public std::enable_shared_from_this<Node> { ...

Then change the offending line to 然后将违规行更改为

que.push(this->shared_from_this());

This causes it to return a shared_ptr that points to the object, but it is shared with the already existing shared_ptr, instead of being two separate shared_ptr objects. 这将导致它返回一个指向该对象的shared_ptr,但是它与已经存在的shared_ptr共享,而不是成为两个单独的shared_ptr对象。

Note, for the use of this->shared_from_this() to be legal, the object must be owned by a shared_ptr. 注意,为了合法使用this->shared_from_this() ,该对象必须由shared_ptr拥有。 You already have accomplished this via the static create() method, but I wanted to make sure you understood the limitation. 您已经通过静态create()方法完成了此操作,但是我想确保您了解该限制。

Edit: A brief explanation of shared_ptr ownership. 编辑: shared_ptr所有权的简要说明。

When you create a shared_ptr from a raw pointer using the constructor, it creates a reference object that contains both a pointer to the object and a reference count, which is used to determine how many shared_ptr objects point to it. 使用构造函数从原始指针创建shared_ptr ,它将创建一个引用对象,该对象既包含指向该对象的指针又包含引用计数,该引用计数用于确定有多少shared_ptr对象指向该对象。 A pointer to this reference object is then passed to all copies that are made from that original shared_ptr , with the reference count keeping track of how many shared_ptr objects refer to it. 然后,将指向该引用对象的指针传递到该原始shared_ptr所创建的所有副本,其中引用计数跟踪引用了多少shared_ptr对象。

When you call shared_ptr(this) , there is no way for the shared pointer to know that this is owned by another shared pointer, and creates a new reference object. 当你调用shared_ptr(this) ,是没有办法的共享指针要知道, this是另一个共享指针拥有,并创建一个新的参考对象。 Once the one of them reaches a reference count of zero, the object will be deleted, despite the other shared_ptr reference object still pointing to it, resulting in a dangling pointer and the error you are seeing. 一旦其中一个达到零引用计数,尽管另一个shared_ptr参考对象仍指向该对象,该对象将被删除,从而导致指针悬空和您看到的错误。

If you only need the children to exist when the parent exists, I would consider changing the Node to simply have a std::vector of other Nodes (remove the pointer). 如果仅在父级存在时才需要子级存在,则可以考虑将Node更改为仅具有其他Node的std::vector (删除指针)。 When the highest level node is destroyed via its destructor, it will destroy the vector, which destroys the children nodes, and so-on. 当最高级别的节点通过其析构函数销毁时,它将销毁向量,从而销毁子节点,依此类推。

class Node
{
  // whatever operations you need... 

  std::vector<Node> children;
}

Edit: As requested... 编辑:根据要求...

If you have a use case where you do really want to have the children outlive the parents, you'll have to deal with the parent pointer, since it could be destroyed before the children. 如果您有一个用例,您确实想让孩子们比父母长寿,那么您就必须处理父母指针,因为它可能在孩子之前被销毁。 One quick solution is determine if you really NEED the parent pointer, and eliminate it if you don't need it. 一种快速的解决方案是确定您是否确实需要父指针,如果不需要,则将其消除。

However, assuming you still want to retain it, you cannot use shared_ptr here. 但是,假设您仍要保留它,则不能在此处使用shared_ptr If you do that, you'll have a circular dependency, and neither will be destroyed automatically, which isn't what you want. 如果这样做,您将具有循环依赖关系,并且两者都不会被自动销毁,这不是您想要的。

The solution here is to use std::weak_ptr . 解决方案是使用std::weak_ptr Basically, it interacts with the shared_ptr reference object in such a way that it doesn't prevent the destruction of the pointed to object. 基本上,它以不防止破坏指向对象的方式与shared_ptr引用对象进行交互。

class Node
{
private:
   std::weak_ptr<Node> parent;
   // Other constructors.  
   Node(int a_value):value(a_value),parent() {} 
public:
   SharedNode addChild(SharedNode child){
        child->parent = this->shared_from_this(); // Initialize their pointer using
                                                  // your shared pointer
        children.push_back(child);
        return child;
   }
   // This function will return a shared_ptr to nullptr (and will evaluate false) 
   // if you have no parent, or if the parent node has been deleted
   SharedNode getParent()
   {
       return parent.lock();
   }
};

Consider what happens with the following code: 考虑以下代码会发生什么:

Node * dumb_ptr = new Node;
shared_ptr<Node> smart1 = dumb_ptr;
shared_ptr<Node> smart2 = dumb_ptr;

You now have two smart pointers both thinking they own the same object. 现在,您有两个智能指针,它们都认为它们拥有相同的对象。 One of them is going to delete the object, and the other one will try to use or delete that deleted object at some point. 其中一个将要删除该对象,而另一个将在某个时候尝试使用或删除该已删除的对象。 The way you fix this is by always creating a smart pointer from another smart pointer or from new . 解决此问题的方法是始终从另一个智能指针或new创建一个智能指针。 Best to never use any dumb pointers at all - that includes this . 最好不要使用任何愚蠢的指针-包括this指针。

shared_ptr<Node> smart1 = new Node;
shared_ptr<Node> smart2 = smart1;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM