使用存储在向量中的对象的正确方法是啥?

Posted

技术标签:

【中文标题】使用存储在向量中的对象的正确方法是啥?【英文标题】:What is the correct way to work with an object that was stored in a vector?使用存储在向量中的对象的正确方法是什么? 【发布时间】:2015-11-08 18:39:40 【问题描述】:

来自 Java,我对如何正确访问存储在向量中的对象感到困惑。

在这个测试用例中,我希望两个输出都显示相同的数字 (2):

#include <iostream>
#include <vector>

using namespace std;

class Item 
public:
  int id = 0;
  Item(int id)
  
    this->id = id;
  
;

int main()

  vector<Item> items;
  Item item = Item(1);
  items.push_back(item);

  Item itemFromVector = items.at(0);
  itemFromVector.id = 2;

  cout << "item: " << item.id << endl;
  cout << "itemFromVector: " <<  itemFromVector.id << endl;

// output:
// item: 1
// itemFromVector: 2

【问题讨论】:

你不需要向量来观察这种现象。 Item item = Item(1); Item item2 = item; item2.id = 2; cout &lt;&lt; item.id &lt;&lt; endl;. 在您的测试用例中,item 是一个对象,itemFromVector 是另一个单独的对象,并且向量存储另一个对象,与上述两个不同。每个人都有自己的数据副本。 好吧,但是你如何在 C++ 中做到这一点?我不应该使用矢量吗? 顺便说一句,Item item = Item(1); 是多余的 javaism。 Item item(1); 就足够了。 【参考方案1】:

在这种情况下,item.id 等于 1。

这背后的原因是push_back 调用将执行向量中对象的副本(向量中的Itemitem 是两个不同的对象)。

观察你想要达到的目标的一种方法是使用指针:

int main()

  vector<Item*> items; //items contains addresses of Item object
  Item item = Item(1);
  items.push_back(&item); // push address of your item

  Item* itemFromVector = items.at(0); // Pointer to item
  itemFromVector->id = 2; // Modify item id attribute

  cout << "item: " << item.id << endl; // Access item id attribute
  cout << "itemFromVector: " <<  itemFromVector->id << endl; // Access item id attribute

【讨论】:

好的,目前正在处理这个......看起来指针的行为更像我在 Java 中的引用所习惯的...... @Zalumon 刚刚添加了一些 cmets 来提供帮助 好的,我想我明白了。在这种情况下,我将使用指针。非常感谢大家的帮助。不幸的是,我只能接受一个答案。我认为 Jérôme 是第一个使用指针解决方案的人。不过,我对所有答案都投了赞成票。【参考方案2】:

当你执行时:

items.push_back(item);

item 的副本存储在向量中。 item 和向量中的副本这两个对象是完全独立的对象。对一个的更改不会影响另一个。

如果您希望能够更改其中一个并期望更改反映在另一个中,您需要创建一个指针向量或std::reference_wrappers 的向量。

#include <iostream>
#include <vector>
#include <functional>

class Item 
   public:
      int id = 0;
      Item(int id)
      
         this->id = id;
      
;

void test1()

   std::cout << "Output from test1() ...\n";
   std::vector<Item*> items;
   Item item(1);
   items.push_back(&item);

   Item* itemFromVector = items.at(0);
   itemFromVector->id = 2;

   std::cout << "item: " << item.id << std::endl;
   std::cout << "itemFromVector: " <<  itemFromVector->id << std::endl;


void test2()

   std::cout << "Output from test1() ...\n";
   std::vector<std::reference_wrapper<Item>> items;
   Item item(1);
   items.push_back(item);

   Item& itemFromVector = items.at(0);
   itemFromVector.id = 20;

   std::cout << "item: " << item.id << std::endl;
   std::cout << "itemFromVector: " <<  itemFromVector.id << std::endl;


int main()

   test1();
   test2();

输出:

Output from test1() ...
item: 2
itemFromVector: 2
Output from test1() ...
item: 20
itemFromVector: 20

【讨论】:

@OliverCharlesworth,感谢您指出错误。我需要彻底检查答案:) @OliverCharlesworth:什么?它接受T const&amp;T&amp;&amp; 的参数,不是吗? @ChristianHackl,该评论是在我彻底修改答案之前。 @ChristianHackl:其实你是对的。但是效果是一样的; vector 将复制要插入的参数。【参考方案3】:
vector<Item> items;

在此代码中,您有一个 Item 类的列表。 当您调用items.push_back(item); 时,您会将item 的副本推送到items 数组中。因此,当您调用 itemFromVector.id = 2; 时,您会更改刚刚放入数组中的副本的 id。

如果要将原始 item 对象复制到数组中并在之后进行编辑,则必须进行以下更改:

vector<Item*> items;
items.push_back(&item);
Item* itemFromVector = items.at(0);
itemFromVector->id = 2;

在第一行中,您告诉向量我们要存储 Item 类的指针,而不是它的副本。 在第二行中,我们通过在变量名前添加&amp;item 的地址推送到我们的数组中。 在第三行中,我们通过在变量名前添加 * 来告诉编译器我们的变量类型是 Item 类的指针。 在第四行中,我们将. 更改为-&gt;,因为我们正在使用指针对象。

【讨论】:

【参考方案4】:

由于您来自 Java,因此在 C++ 中使用 references 来获得预期的行为对您来说并不难。

项目项目 = 项目(1);

与 Java 不同,这里的 item 不是一个引用,而是一个实际的实例。你可以在没有赋值的情况下立即构造它:

Item item(1);

items.push_back(item);

在这里,您需要知道您在向量中推送的内容不是对项目的引用,而是它的副本,一个从项目复制的真实新实例。

Item itemFromVector = items.at(0);

在这里,您将 itemFromVector 执行为向量中第一个项目的 reference。但实际上它是从它复制的一个实例。如果要引用,就声明为引用,就这么简单:

Item& itemFromVector = items.at(0);

总之,在使用 Java 时,一切都是参考。这在 C++ 中是不正确的。只有明确声明为引用时,引用才是引用。

【讨论】:

这更像是 Java 没有 C++ 风格的引用,Java 所谓的“引用”(NullPointerException 除外)将是 C++ 中的指针。 Java 中的 everything 不是引用(或指针),因为原语不是。 @ChristianHackl 一切都是参考只是一句格言,不应从字面上理解。它适用于为操作对象而定义的变量,因为不使用 new 就不能“直接”(实际上是在堆栈上)实例化对象。好吧,再说一遍,这只是一句用来解释与 C++ 的主要区别的格言。 :) 可能会造成混淆的是,在 C++ 术语中,“对象”具有更广泛的含义,并且包含原始类型的实例。 @ChristianHackl 在 C++ 中?还是在 Java 中? 在 C++ 中。在像int x = 0; 这样的行中,谈论“int 对象”是正确的。【参考方案5】:
  Item(int id)
  
    this->id = id;
  

这是典型的 Java 主义。惯用的 C++ 方式是:

Item(int id) : id(id) 
Item item = Item(1);

这个也是。惯用的 C++ 方式是:

Item item(1);
items.push_back(item);

这会创建item副本,并将副本存储在向量中。

Item itemFromVector = items.at(0);

同样,这会创建第一项的副本并将其存储在itemFromVector 变量中。

您可能会想到 Java 代码,例如:

List<Item> list = new ArrayList<Item>();
list.add(item);
Item item = list.get(0);

但这是错误的类比。 Java 只是将指针存储在列表中。更合适的 Java 类比是:

List<Item> list = new ArrayList<Item>();
list.add(item.clone());
Item item = list.get(0).clone();

如果您希望 C++ 表现出 Java list.add(item); Item item = list.get(0); 的行为,那么您必须创建一个指针向量:

  vector<Item*> items;
  Item item(1);
  items.push_back(&item);

  Item* itemFromVector = items.at(0);
  itemFromVector->id = 2;

但是 C++ 不是 Java,所以这不是一件常见的事情,并且会导致与 C++ 没有任何内置垃圾收集这一事实相关的各种问题。作为一般准则,请尽可能避免使用指针,并特别注意指向本地对象的指针。

【讨论】:

Okidoki,谢谢你在java中的解释,这让我很清楚;-)。但现在我觉得我回到了第一方。如果您不鼓励这种解决方案,那么您有什么建议? @Zalumon:不客气 :) 但是您在寻找什么解决方案?真正的问题是什么? 我的意思是,如果不鼓励指针接近您和概述的其他人,c++ 程序员通常如何处理对象列表? @Zalumon:推荐的方法是只使用T 的向量而不是T* 的向量,直到遇到特定问题并需要更复杂的东西。我的例子是一个特别危险的例子,因为我使用了一个指向本地对象的指针,当本地对象被销毁但指向它的指针仍然挂在其他地方时,这可能会导致问题。请问您认为指针方法如此吸引人的原因是什么? @Zalumon:我认为你应该尝试更多地了解指针、引用和单个对象的动态内存分配。对于容器和一切,可能更难理解。 (另一方面,老实说,我不确定这些天我将如何教授 C++。首先是低级基础还是有用的抽象?)无论如何,如果 Java 程序员在 C++ 中一直犯错,那么这是他们过度使用指针和new

以上是关于使用存储在向量中的对象的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

我是否正确删除了指向对象的向量?

定义生成元素向量C ++的函数时正确的方法是啥

在 laravel 中将“历史数据”存储到数据库中的正确方法是啥?

检查联合实例之间是不是相等的正确方法是啥?

C++ OOP,输出对象指针向量时的空白控制台,不确定填充向量是不是正确

将“this”传递给新对象的内联重写方法的正确方法是啥? [复制]