c++ 中的矢量元素始终设置为最后一个元素
Vector elements in c++ always set to last element
我想将类的成员存储到向量中。在我的类中,我有一些私有变量,我通过常量指针访问它们,以便保护它们不被更改(见这篇文章)。问题:当我在循环期间将类的实例添加到向量中并在之后访问它们时,所有元素似乎是最后一个添加的。这是MWE:
#include <iostream>
#include <vector>
using namespace std;
class myClass {
private:
int x_;
void init(int x) {
x_ = x;
}
public:
const int &x;
myClass(int x) : x(x_) {
init(x);
};
};
int main(int argc, const char * argv[]) {
vector<myClass> myVector;
// Does not work
for (int j=0; j<3; j++) {
myVector.push_back(myClass(j));
}
for (int j=0; j<3; j++) {
cout << "1st attempt: " << myVector.at(j).x << endl;
}
myVector.clear();
// Works
for (int j=0; j<3; j++) {
myVector.push_back(myClass(j));
cout << "2nd attempt: " << myVector.at(j).x << endl;
}
myVector.clear();
// Works also
myVector.push_back(myClass(0));
myVector.push_back(myClass(1));
myVector.push_back(myClass(2));
for (int j=0; j<3; j++) {
cout << "3rd attempt: " << myVector.at(j).x << endl;
}
return 0;
}
问题:我做错了什么,我可以修复它吗?
我做错了什么,我可以修复它吗?
好吧,这个想法总体上看起来是错误的。 撇开这一点:如果你想"修复它",你需要考虑当你push_back
时发生了什么:
for (int j=0; j<3; j++) {
myVector.push_back(myClass(j));
}
您按值传递类,因此调用复制构造函数。 因此push_back没有得到myClass(j)
,而是得到了一份副本。 然而你没有写一个复制构造函数...那么,您的复制构造函数从何而来? 你得到的是编译器默认值,它只是说副本的int &x
获得与原始对象相同的值(而不是新对象的x_)。
这意味着向量中副本内部的引用是指向您作为myClass(j)
传入的原始对象。 然而,该原始参数是在push_back
调用完成后释放的。 每次通过循环时,内存空间可能会被重用,这就是您看到最后一个过时值的原因。 尝试通过 Valgrind 或类似方式运行它,它可能会发现一个问题。
您可以通过编写自己的复制构造函数来解决此问题,该构造函数执行您的实际含义:
class myClass {
private:
int x_;
void init(int x) {
x_ = x;
}
public:
const int &x;
myClass(int x) : x(x_) {
init(x);
};
myClass(myClass const & other) : x_ (other.x_), x(x_) {
}
};
通过这种方式,您可以明确地告诉编译器您将int const &
链接到int
的隐含愿望。 如果你不告诉它,它根本无法知道它们是相关的。
但是可能有很多原因首先不做这种界面。 只需使用普通的访问器方法,除非您已经想到了这样做的真正充分的理由。
(还要注意@JoachimPileborg对emplace_back的建议,很高兴知道这一点。
扩展我的评论...
当你这样做时
myVector.push_back(myClass(0));
你创建一个临时对象myClass(0)
,一个对象在push_back
函数返回后被销毁。这个临时对象被复制到向量中,我认为副本中x
的成员变量将引用临时对象的成员变量x_
。当然,当您以后使用x
时,这会导致未定义的行为,该现在引用了已破坏对象中的数据。
有两种方法可以解决此问题:
- 使用
emplace_back
而不是push_back
。 - 添加一个复制构造函数,使
x
引用对象自己的x_
变量。
当您将对象添加到vector
时,该对象将被复制,并调用该类的复制构造函数。由于myClass
具有引用成员,因此默认复制构造函数不适用于所需的语义,因此需要定义自己的语义:
myClass (const myClass& rhs) : x(x_) {
init(rhs.x);
}
当您使用它时,您可能还希望定义一个赋值运算符:
myClass& operator= (const myClass& rhs) {
x_ = rhs.x;
return *this;
}
通过修复myClass的定义很容易修复,因此:
class myClass {
private:
public:
const int x;
myClass(int x_) : x(x_) {
};
};
合理的:
myClass 实现了数据即接口习惯用法,这很好,但如果你要公开数据,请通过公开实际数据来公开数据 - 而不是对它的引用。
请记住,引用实际上是下面的指针,因此它包含一个内存地址,并且它在类中的存在会阻止自动生成移动构造函数。
- 在QComboBox或QListWidget中设置所选元素的样式文本?
- 如果我在相应 char 数组的声明中为其提供额外的元素,是否会自动设置 NULL?
- 无法在构造函数中执行设置元素插入
- 循环中的变量被设置为下一个数组的元素始终具有相同的内存地址?
- 为 unordered_map 中的元素设置默认构造函数(如果是 [] 运算符)
- 在C++中将指针数组的所有元素设置为 nullptr
- C++:如何将枚举元素设置为无限?
- 如何在类中初始化数组并为第一个元素设置值
- 如何将Bools中的一个元素设置为true
- 如何将布尔矩阵的每个元素设置为false而不循环
- 将数组中的元素设置为 0 或 NULL
- 使用 while / do-while 循环进行数组元素设置
- C++:数组元素设置为0
- 将元素设置在两个尺寸 std::vector
- 为什么将地图的元素设置为其大小会在分配之前增加大小*
- 如何简洁地将cv::Mat中的每个元素设置为一些标量,如cvSet
- c++默认初始化是否将数组元素设置为默认值?
- 如何将数组中的每个元素设置为0
- 将结构体元素设置为定义中的值
- C++ DirectX11 将同一矢量中的元素设置为同一更新循环中的不同位置