使用shared_ptr的引用时的SIGSEGV

时间:2018-10-21 06:55:05

标签: c++

如果运行以下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];

问题将消失。程序正确输出两个零并安全退出,为什么?是什么原因导致了最初的崩溃,以及为什么修改可以解决该问题?

2 个答案:

答案 0 :(得分:3)

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的引用也不会无效。

答案 1 :(得分:2)

newNode中将addChild推到此处时:

parentNode->childNodes.push_back(newNode);

您可能会使对main的引用parentNode->childNodes[0]无效,而在node中将其称为addChild

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

node->childNodes

依次触发段故障。


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

请注意,指针本身(包含在std::vector中的指针)总是可以的,因为它们从未在两个版本中进行过修改(当您创建{ {1}}在可行的情况下,由于std::shared_ptr提供的引用计数机制,一切都可以解决。