在 class 方法之外返回共享指针引用中断

Returning shared pointer reference breaks outside of class methods

我在 C++ 中有一棵树,树中有一个方法,每当将新叶子添加到树中时,returns 都会引用一个新叶子 shared_ptr。当我在 Tree class 的其他方法中使用它时,它工作正常,但是当我尝试从 main 调用它时,它崩溃了,尽管代码似乎在做同样的事情。

这是节点和树 classes:

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

class Node
{
public:
    int value;
    std::vector<std::shared_ptr<Node> > children; 
    Node(int value): value{value} {}
};

class Tree
{
public:
    std::shared_ptr<Node> root;
    Tree(): root {nullptr} {}

    std::shared_ptr<Node> CreateLeaf(int value)
    {
        return std::make_shared<Node>(value);
    }

    std::shared_ptr<Node>& AddLeaf(int value, std::shared_ptr<Node>& ptr)
    {
        if(ptr == nullptr)
        {
            ptr = std::move(CreateLeaf(value));
            return ptr;
        }
        else
        {
            std::shared_ptr<Node> newLeaf = CreateLeaf(value);
            ptr->children.push_back(std::move(newLeaf));
            return ptr->children.back();
        }
    }

    void otherMethod()
    {
        AddLeaf(1, root);
        std::shared_ptr<Node>& temporary = AddLeaf(2, root);
        std::cout << "temporary->value: " << temporary->value << std::endl;
    }

};

如果主要功能是:

int main()
{
    Tree t;
    t.otherMethod();
}

那么程序就可以正常运行了。

但是,如果主要功能是:

int main()
{
    Tree t;
    t.AddLeaf(1, t.root);
    std::shared_ptr<Node>& b = t.AddLeaf(2, t.root);
    std::cout << "b->value = " << b->value << std::endl; 

}

程序崩溃了,尽管它做了几乎相同的事情。 AddLeaf 似乎只是将 nullptr 存储在 b 中,尽管它是对持久对象 t.root->children[0] 的引用。为什么要这样做?

引用矢量或任何自动调整大小的容器中的元素是危险的。当我用 C++ 创建我的第一个游戏时,我已经处理过这个问题。

基本上发生的事情是:

您的矢量在添加新的 shared_ptr 时会调整大小,这可能会导致操作分配更多内存。在此过程中,当前存在的向量被破坏并分配到内存中可能 不同的 位置。
这意味着所有当前存在的指针或对向量中元素的引用都将失效,并且可能会在某个时候使您的程序崩溃。

在向量中传递对 shared_ptr 的引用基本上会促进未定义的行为。更明智的做法是只 return 一个新的 shared_ptr 并让引用计数器递增。
这样,当向量调整大小时,程序中的引用不会失效。

删除引用应该有所帮助:

std::shared_ptr<Node> AddLeaf(int value, std::shared_ptr<Node>& ptr)
{
    if(ptr == nullptr)
    {
        ptr = std::move(CreateLeaf(value));
        return ptr;
    }
    else
    {
        std::shared_ptr<Node> newLeaf = CreateLeaf(value);
        ptr->children.push_back(std::move(newLeaf));
        return ptr->children.back();
    }
}

无论如何,正如我所见,您正在创建一棵树,也许您想看一下我在一个(废弃的)项目中编写的代码:https://github.com/wvanbreukelen/LexMe/blob/feature-tree-node-vector-specialization/LexMe/TreeNode.h
这是一个对移动语义和迭代器友好的通用树的非常完整的实现。