如何将带有std :: vector <class>成员的std :: move类移动到另一个向量<class>中?

时间:2017-03-11 20:27:33

标签: c++ class stdvector

我试图线性化“节点”的层次结构。将类转换为单个(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;
}

这会导致程序崩溃。崩溃前的输出是:

  

我确实进行了线性化   线性化开始了   运行循环0 of 2
  线性化开始了   Runnning loop 0 of 2
  线性化开始了   完成节点线性化。
  完成节点线性化。
  运行循环1 of 18446744073709191157
  线性化开始了   运行循环0 of 1011712
  线性化开始了。

我试图在这里找到一个优雅而有效的解决方案。节点&#39;节点&#39; class可以变大并包含许多其他类和向量。鉴于数据大小,我不愿构建移动构造函数/赋值来覆盖所有数据结构。

我想要做的就是使用这段代码:

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 / groups):

  1. 如果调用默认移动构造函数,为什么节点没有正确移动到ArrayOfNodes?不是默认的移动构造函数调用每个成员的移动构造函数,并且std :: vector无论如何都有内部指针,所以在移动时它仍应指向相同的数据吗?我误解了这个过程的哪个部分?

  2. 对于这种情况(线性化),什么是标准/优秀/经验的编码器解决方案?

  3. 欢迎任何和所有评论,这是我的第一个问题,如果我做错了或者可以做得更好,请告诉我。谢谢!

1 个答案:

答案 0 :(得分:0)

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*>,并用指向节点的指针填充它。但那可能不是你追求的目标。