You would get a SIGSEGV if you ran the following C++ code:
#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;
}
I don't know why it crashes. But I found that if I changed this line in the main function:
std::shared_ptr<Node>& childNode = parentNode->childNodes[0];
to
std::shared_ptr<Node> childNode = parentNode->childNodes[0];
The problem would disappear. The program correctly output two zeros and exited safely, why? What caused the initial crash and why the modification could fix it?
A push_back
into a vector invalidates all iterators, pointers and references to existing elements of the vector.
Since node
is a reference to an element of parentNode->childNodes
, pushing into it invalidates that reference. So merely accessing node->childNodes.size()
is undefined behavior.
When you use a copy of the element in the vector, the node
reference remains valid because the shared_ptr
it refers to is still there, outside the storage the vector manages.
You can also avoid copying the shared_ptr
by simply passing a reference to the node itself. Ie
void addChild(const Node &node, std::shared_ptr<Node> &parentNode)
Even if any shared_ptr gets reallocated, the reference to Node
will not be invalidated.
When you push the newNode
here in addChild
:
parentNode->childNodes.push_back(newNode);
You may be invalidating the reference you took on main
to parentNode->childNodes[0]
, which in turn is called node
in addChild
.
In the end, this results in an invalid read when you dereference node
in your problematic line:
node->childNodes
which triggers the segfault in turn.
If, instead, you create a copy of parentNode->childNodes[0]
, everything is fine, because in this case you do not have any reference to any element of the childNodes
std::vector
, so none can go invalid.
Note that the pointers themselves (the ones contained in your std::vector
s) are always fine, because they are never modified in either version (and when you create a copy of the std::shared_ptr
in the working case, everything works out on destruction as well due to the reference counting mechanics that std::shared_ptr
provides).
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.