使用存储在向量中的对象的正确方法是啥?
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 << item.id << endl;
.
在您的测试用例中,item
是一个对象,itemFromVector
是另一个单独的对象,并且向量存储另一个对象,与上述两个不同。每个人都有自己的数据副本。
好吧,但是你如何在 C++ 中做到这一点?我不应该使用矢量吗?
顺便说一句,Item item = Item(1);
是多余的 javaism。 Item item(1);
就足够了。
【参考方案1】:
在这种情况下,item.id
等于 1。
这背后的原因是push_back
调用将执行向量中对象的副本(向量中的Item
和item
是两个不同的对象)。
观察你想要达到的目标的一种方法是使用指针:
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_wrapper
s 的向量。
#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&
或T&&
的参数,不是吗?
@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
类的指针,而不是它的副本。
在第二行中,我们通过在变量名前添加&
将item
的地址推送到我们的数组中。
在第三行中,我们通过在变量名前添加 *
来告诉编译器我们的变量类型是 Item
类的指针。
在第四行中,我们将.
更改为->
,因为我们正在使用指针对象。
【讨论】:
【参考方案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
。以上是关于使用存储在向量中的对象的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章
在 laravel 中将“历史数据”存储到数据库中的正确方法是啥?