选择和修改嵌套向量中的条目的最佳实践

Posted

技术标签:

【中文标题】选择和修改嵌套向量中的条目的最佳实践【英文标题】:best practice to select and modify entries in nested vectors 【发布时间】:2020-02-12 13:55:02 【问题描述】:

我正在寻找一种有效的方法来解决以下(可能很容易)问题:

我有一个A 类型的向量。 A 类包含B 类型的向量。 我无法更改课程A或设计 A 类的向量(我无法将其设为指向 A 的指针向量 例如)。

我想要一个 B 类的向量,其中包含 B 的所有条目,这些条目包含在具有特殊属性的 A 向量中。

我想阅读这个问题有点复杂,但如果你看到一个例子可能更容易理解:

#include <vector>
#include <iostream>
using namespace std;

struct B 
  int n;
  double val;
;

struct A 
  vector<B> v;
;

int main() 
  // generate some dummy data:
  A a1;
  a1.v.push_back(B1, 1.0);
  a1.v.push_back(B2, 2.0);
  a1.v.push_back(B1, 3.5);

  A a2;
  a1.v.push_back(B2, 2.0);
  a1.v.push_back(B3, 1.0);
  a1.v.push_back(B1, 2.5);

  // this is my initial situation: a vector of type A
  vector<A> va;
  va.push_back(a1);
  va.push_back(a2);

  // what I want to get is a vector of type B with all B values whose n == 1
  vector<B> vb;

  // possible solution to get all elements of B
  for(auto &any : va)
    for(auto &b : any.v)
      if(b.n == 1)
        vb.push_back(b);
      
    
  

  for(const auto &any:vb)
    cout << any.val << endl;
  

  return 0;

问题:

我想获得B 类的向量而不复制每个元素。稍后,我需要修改向量B中的元素,它们也应该在向量A中修改(意思是,如果我更改vb[0].val = 100;,则va[0].v[0]中的条目也应该是100

我考虑过使用指针来执行此操作,但我不确定这是否是个好主意,因为向量 va 的元素是在堆栈上而不是在堆上分配的。

** 编辑:**

我可以确保只要我需要B 的向量,我就不会以任何方式修改A 的向量。

【问题讨论】:

对不起,您无法避免复制。 C++ 不能以这种方式工作。您也许可以创建一个std::reference_wrappers 的向量,并可能完成类似的事情,但是,就拥有Bs 的向量而言,没有办法避免复制。这也许是 Java 中对象的工作方式,但 C++ 不是 Java。 "因为向量 va 的元素是在栈上而不是在堆上分配的。" 向量元素永远不会在栈上。也许这就是你的误解:在va.push_back(a1); 之后,向量包含a1 的副本,但不是a1 本身 除了@idclev463035818 的评论:当您使用非拥有指针时,对象的存储类是什么并不重要。它们总是可以使用的。 ...但考虑到指向元素的指针是不稳定的。即,如果您在向量中添加/删除某些内容,它们就会失效 稍后,我需要修改向量 B 中的元素,它们也应该在向量 A 中进行修改 你为什么不在需要的时候直接 for(auto &amp;any : va) for(auto &amp;b : any.v) if(b.n == 1) do thing here 呢? 【参考方案1】:

在堆栈上有va 不一定是问题,只要您将va 移动到它的新位置。在这种情况下,您的“移动到”向量将在内部指向由“移动自”va 向量分配的存储空间(并且原始的 va 将变为空)。

但有一个问题是,如果您在已经创建了指向 Bs 的指针之后将新的 Bs 添加到 A。如果Av 向量没有足够的容量来存储新插入的B,则它必须将其所有B 元素重新分配到新的存储位置。这反过来又会改变所有B 元素的地址。

因此,只要您能保证,您就可以创建指针:

va 被移动而不是被复制。 初始化后没有新的Bs 被添加到A

假设以上可以保证,你可以创建你的vb如下:

std::vector<B *> vb;

for (auto & a : va) 
  for (auto & b : a.v) 
    if (b.n == 1)  vb.push_back(&b); 
  

或使用std::reference_wrapper:

std::vector<std::reference_wrapper<B>> vb;

for (auto & a : va) 
  std::copy_if(a.v.begin(), a.v.end(),
    std::back_inserter(vb),
    [](auto const & b)  return b.n == 1; );

【讨论】:

nitpick,将 std::for_each 与 std::copy_if 结合使用会使这变得完美。 我个人更喜欢这里的范围。看起来更干净恕我直言。 谢谢,我可以确保在需要vb时不再修改va @theWiseBro 我个人认为std::for_each 不会比使用基于范围的 for 循环增加任何额外的可读性,但我理解你为什么要在这里使用它。 似乎并不完全清楚移动向量不会使迭代器无效,请参阅LWG issue 2321(如果分配器的propagate_on_container_move_assignmentfalse,它肯定会失效)。

以上是关于选择和修改嵌套向量中的条目的最佳实践的主要内容,如果未能解决你的问题,请参考以下文章

Kafka 的嵌套 Avro 类型是不是有最佳实践?

Rails 4 [最佳实践] 嵌套资源和浅层:true

Swift NSFetchedResultsController 嵌套对象的最佳实践

导入深度嵌套的 Javascript 组件的最佳实践

最佳实践:将派生类的向量传递给基类向量上的方法

逻辑或查找表:最佳实践