如何将 std::move class 和 std::vector<class> 成员转换成另一个 vector<class>?
How to std::move class with std::vector<class> member into another vector<class>?
我正在尝试将 'Node' classes 的层次结构线性化为单个 (std::vector) 数组。这是演示问题的完整 C++ 程序代码,已尽可能简化:
#include <iostream>
#include <vector>
struct Node;
struct B{
int nvar1;
std::vector<Node> Children;
};
struct Node{
B bvar1;
};
void Linearize(Node & NODE, std::vector<Node> & ArrayOfNodes){
std::cout<<"Linearizing started.\n";
ArrayOfNodes.push_back(std::move(NODE));
Node & node = ArrayOfNodes.back();
for(int n = 0; n < node.bvar1.Children.size(); n++){
std::cout<<"Running loop "<<n<<" of "<<node.bvar1.Children.size()<<"\n";
Linearize(node.bvar1.Children[n], ArrayOfNodes);
}
std::cout<<"Done with node linearization.\n";
}
int main(){
Node ParentNode;
//Fill the ParentNode
ParentNode.bvar1.nvar1 = 0;
ParentNode.bvar1.Children.resize(2);
ParentNode.bvar1.Children[0].bvar1.Children.resize(2);
ParentNode.bvar1.Children[0].bvar1.Children[0].bvar1.nvar1 = 1;
ParentNode.bvar1.Children[0].bvar1.Children[1].bvar1.nvar1 = 2;
ParentNode.bvar1.Children[1].bvar1.nvar1 = 3;
std::cout<<"I do get to the linearizing.\n";
std::vector<Node> ArrayOfNodes;
Linearize(ParentNode, ArrayOfNodes);
std::cout<<"I do get to the displaying part.\n";
for(int n = 0; n < ArrayOfNodes.size(); n++){
std::cout<<ArrayOfNodes[n].bvar1.nvar1<<"\n";
}
return 0;
}
这会使程序崩溃。崩溃前的输出是:
I do get to the linearizing.
Linearizing started.
Running loop 0 of 2
Linearizing started.
Runnning loop 0 of 2
Linearizing started.
Done with node linearization.
Done with node linearization.
Running loop 1 of 18446744073709191157
Linearizing started.
Running loop 0 of 1011712
Linearizing started.
我想在这里找到一个优雅高效的解决方案。 'Node' class 可以变大并包含许多其他 classes 和向量。考虑到数据大小,我不愿意构造 move constructors/assignments 来覆盖所有数据结构。
我想做的事情将适用于此代码:
void Linearize(Node & NODE, std::vector<Node> & ArrayOfNodes){
std::cout<<"Linearizing started.\n";
ArrayOfNodes.push_back(NODE);
Node & node = ArrayOfNodes.back();
for(int n = 0; n < NODE.bvar1.Children.size(); n++){
std::cout<<"Running loop "<<n<<" of "<<node.bvar1.Children.size()<<"\n";
Linearize(NODE.bvar1.Children[n], ArrayOfNodes);
}
std::cout<<"Done with node linearization.\n";
}
但是当我想移动它时,那会复制东西。我希望它比这更有效率。
基本上是两个问题(s/组):
如果调用默认移动构造函数,为什么节点不能正确移动到 ArrayOfNodes?默认的移动构造函数不是调用每个成员的移动构造函数,而且 std::vector 里面有指针,所以移动时它应该仍然指向相同的数据?我误解了流程的哪一部分?
对于这种情况(线性化),standard/good/veteran 编码器的解决方案是什么?
欢迎任何评论,这是我的第一个问题,如果我做错了什么或者可以做得更好,请告诉我。谢谢!
发生的事情是 node
是对您正在构建的 std::vector
中的项目的引用(即指针)。获取引用后,您在向量上使用 push_back
,这将增加底层数组,因此可能会使所有指向它的指针无效(增加数组通常意味着分配一个新的、更大的内存块,并将所有数据移动到它)。当您随后想要访问节点的下一个子节点时,您正在引用释放的内存。
有 3 种方法可以解决这个问题。首先,在开始线性化过程之前预先分配数组:
std::vector<Node> ArrayOfNodes;
ArrayOfNodes.reserve(numberOfNodes); // <-- you need to be able to determine this
Linearize(ParentNode, ArrayOfNodes);
第二种解决方案是在移动节点之前将子项推到向量上:
void Linearize(Node & NODE, std::vector<Node> & ArrayOfNodes){
std::cout<<"Linearizing started.\n";
for(int n = 0; n < NODE.bvar1.Children.size(); n++){
std::cout<<"Running loop "<<n<<" of "<< NODE.bvar1.Children.size()<<"\n";
Linearize(NODE.bvar1.Children[n], ArrayOfNodes);
}
ArrayOfNodes.push_back(std::move(NODE));
std::cout<<"Done with node linearization.\n";
}
第三种解决方案是不引用向量中的节点,而是获取其索引:
void Linearize(Node & NODE, std::vector<Node> & ArrayOfNodes){
std::cout<<"Linearizing started.\n";
size_t index = ArrayOfNodes.size();
ArrayOfNodes.push_back(std::move(NODE));
for(int n = 0; n < ArrayOfNodes[index].bvar1.Children.size(); n++){
std::cout<<"Running loop "<<n<<" of "<<ArrayOfNodes[index].bvar1.Children.size()<<"\n";
Linearize(ArrayOfNodes[index].bvar1.Children[n], ArrayOfNodes);
}
std::cout<<"Done with node linearization.\n";
}
一种完全不同的方法是根本不移动节点,而是构造一个 std::vector<Node*>
,并用指向节点的指针填充它。但这可能不是你想要的。
我正在尝试将 'Node' classes 的层次结构线性化为单个 (std::vector) 数组。这是演示问题的完整 C++ 程序代码,已尽可能简化:
#include <iostream>
#include <vector>
struct Node;
struct B{
int nvar1;
std::vector<Node> Children;
};
struct Node{
B bvar1;
};
void Linearize(Node & NODE, std::vector<Node> & ArrayOfNodes){
std::cout<<"Linearizing started.\n";
ArrayOfNodes.push_back(std::move(NODE));
Node & node = ArrayOfNodes.back();
for(int n = 0; n < node.bvar1.Children.size(); n++){
std::cout<<"Running loop "<<n<<" of "<<node.bvar1.Children.size()<<"\n";
Linearize(node.bvar1.Children[n], ArrayOfNodes);
}
std::cout<<"Done with node linearization.\n";
}
int main(){
Node ParentNode;
//Fill the ParentNode
ParentNode.bvar1.nvar1 = 0;
ParentNode.bvar1.Children.resize(2);
ParentNode.bvar1.Children[0].bvar1.Children.resize(2);
ParentNode.bvar1.Children[0].bvar1.Children[0].bvar1.nvar1 = 1;
ParentNode.bvar1.Children[0].bvar1.Children[1].bvar1.nvar1 = 2;
ParentNode.bvar1.Children[1].bvar1.nvar1 = 3;
std::cout<<"I do get to the linearizing.\n";
std::vector<Node> ArrayOfNodes;
Linearize(ParentNode, ArrayOfNodes);
std::cout<<"I do get to the displaying part.\n";
for(int n = 0; n < ArrayOfNodes.size(); n++){
std::cout<<ArrayOfNodes[n].bvar1.nvar1<<"\n";
}
return 0;
}
这会使程序崩溃。崩溃前的输出是:
I do get to the linearizing.
Linearizing started.
Running loop 0 of 2
Linearizing started.
Runnning loop 0 of 2
Linearizing started.
Done with node linearization.
Done with node linearization.
Running loop 1 of 18446744073709191157
Linearizing started.
Running loop 0 of 1011712
Linearizing started.
我想在这里找到一个优雅高效的解决方案。 'Node' class 可以变大并包含许多其他 classes 和向量。考虑到数据大小,我不愿意构造 move constructors/assignments 来覆盖所有数据结构。
我想做的事情将适用于此代码:
void Linearize(Node & NODE, std::vector<Node> & ArrayOfNodes){
std::cout<<"Linearizing started.\n";
ArrayOfNodes.push_back(NODE);
Node & node = ArrayOfNodes.back();
for(int n = 0; n < NODE.bvar1.Children.size(); n++){
std::cout<<"Running loop "<<n<<" of "<<node.bvar1.Children.size()<<"\n";
Linearize(NODE.bvar1.Children[n], ArrayOfNodes);
}
std::cout<<"Done with node linearization.\n";
}
但是当我想移动它时,那会复制东西。我希望它比这更有效率。
基本上是两个问题(s/组):
如果调用默认移动构造函数,为什么节点不能正确移动到 ArrayOfNodes?默认的移动构造函数不是调用每个成员的移动构造函数,而且 std::vector 里面有指针,所以移动时它应该仍然指向相同的数据?我误解了流程的哪一部分?
对于这种情况(线性化),standard/good/veteran 编码器的解决方案是什么?
欢迎任何评论,这是我的第一个问题,如果我做错了什么或者可以做得更好,请告诉我。谢谢!
发生的事情是 node
是对您正在构建的 std::vector
中的项目的引用(即指针)。获取引用后,您在向量上使用 push_back
,这将增加底层数组,因此可能会使所有指向它的指针无效(增加数组通常意味着分配一个新的、更大的内存块,并将所有数据移动到它)。当您随后想要访问节点的下一个子节点时,您正在引用释放的内存。
有 3 种方法可以解决这个问题。首先,在开始线性化过程之前预先分配数组:
std::vector<Node> ArrayOfNodes;
ArrayOfNodes.reserve(numberOfNodes); // <-- you need to be able to determine this
Linearize(ParentNode, ArrayOfNodes);
第二种解决方案是在移动节点之前将子项推到向量上:
void Linearize(Node & NODE, std::vector<Node> & ArrayOfNodes){
std::cout<<"Linearizing started.\n";
for(int n = 0; n < NODE.bvar1.Children.size(); n++){
std::cout<<"Running loop "<<n<<" of "<< NODE.bvar1.Children.size()<<"\n";
Linearize(NODE.bvar1.Children[n], ArrayOfNodes);
}
ArrayOfNodes.push_back(std::move(NODE));
std::cout<<"Done with node linearization.\n";
}
第三种解决方案是不引用向量中的节点,而是获取其索引:
void Linearize(Node & NODE, std::vector<Node> & ArrayOfNodes){
std::cout<<"Linearizing started.\n";
size_t index = ArrayOfNodes.size();
ArrayOfNodes.push_back(std::move(NODE));
for(int n = 0; n < ArrayOfNodes[index].bvar1.Children.size(); n++){
std::cout<<"Running loop "<<n<<" of "<<ArrayOfNodes[index].bvar1.Children.size()<<"\n";
Linearize(ArrayOfNodes[index].bvar1.Children[n], ArrayOfNodes);
}
std::cout<<"Done with node linearization.\n";
}
一种完全不同的方法是根本不移动节点,而是构造一个 std::vector<Node*>
,并用指向节点的指针填充它。但这可能不是你想要的。