SIGSEGV 使用shared_ptr引用时

SIGSEGV when using references of shared_ptr

本文关键字:引用 ptr 使用 shared SIGSEGV      更新时间:2023-10-16

如果你运行以下C++代码,你会得到一个SIGSEGV:

#include <iostream>
#include <vector>
#include <memory>
class Node {
public:
std::vector<std::shared_ptr<Node>> childNodes;
};
void addChild(const std::shared_ptr<Node> &node, std::shared_ptr<Node> &parentNode) {
std::shared_ptr<Node> newNode = std::make_shared<Node>();
std::cout << node->childNodes.size() << std::endl;
parentNode->childNodes.push_back(newNode);
std::cout << node->childNodes.size() << std::endl; // the program crashes when running this line
}
int main(int argc, char *argv[]) {
std::shared_ptr<Node> parentNode = std::make_shared<Node>();
parentNode->childNodes.emplace_back(std::make_shared<Node>());
std::shared_ptr<Node>& childNode = parentNode->childNodes[0];
addChild(childNode, parentNode);
return 0;
}

我不知道为什么它会崩溃。但是我发现如果我在主函数中更改这一行:

std::shared_ptr<Node>& childNode = parentNode->childNodes[0];

std::shared_ptr<Node> childNode = parentNode->childNodes[0];

问题会消失。程序正确输出两个零并安全退出,为什么?是什么导致了最初的崩溃,为什么修改可以修复它?

向量push_back会使向量的所有迭代器、指针和对现有元素的引用无效。

因为node是对parentNode->childNodes元素的引用,推入它会使该引用无效。因此,仅访问node->childNodes.size()是未定义的行为。

在矢量中使用元素的副本时,node引用仍然有效,因为它引用的shared_ptr仍然存在,在矢量管理的存储之外。

还可以通过简单地传递对节点本身的引用来避免复制shared_ptr。 即

void addChild(const Node &node, std::shared_ptr<Node> &parentNode)

即使重新分配了任何shared_ptr,对Node的引用也不会失效。

当您在addChild中推送newNode时:

parentNode->childNodes.push_back(newNode);

你可能会使你对parentNode->childNodes[0]main引用无效,这反过来在addChild中被称为node

最后,当您取消引用有问题的行中的node时,这会导致无效读取:

node->childNodes

这反过来触发段错误。


相反,如果您创建parentNode->childNodes[0]的副本,一切都很好,因为在这种情况下,您没有任何对childNodesstd::vector任何元素的引用,因此没有一个可以无效。

请注意,指针本身(包含在std::vector中的指针(总是没问题的,因为它们在任何版本中都不会被修改(当您在工作案例中创建std::shared_ptr的副本时,由于std::shared_ptr提供的引用计数机制,一切都在销毁方面进行(。