在 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
这是一个对移动语义和迭代器友好的通用树的非常完整的实现。
我在 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
这是一个对移动语义和迭代器友好的通用树的非常完整的实现。