c ++中的向量元素始终设置为最后一个元素

时间:2015-05-06 09:13:15

标签: c++ vector

我想将类的成员存储到向量中。在我的课堂上,我有一些私有变量,我通过常量指针访问它们,以防止它们被更改(see this post)。问题:当我在循环期间将类的实例添加到向量中并随后访问它们时,所有元素似乎都是最后添加的元素。这是一个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;
}

显而易见的问题:我做错了什么,能解决吗?

4 个答案:

答案 0 :(得分:3)

  

我做错了什么,能解决吗?

这个想法总的来说是错误的。把它放在一边:如果你想“修复它”,你需要考虑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的建议,这是很好的了解。)

答案 1 :(得分:2)

扩展我的评论......

当你这样做时

myVector.push_back(myClass(0));

使用myClass(0)创建临时对象,该对象在push_back函数返回后被破坏。这个临时对象被复制到向量中,我认为副本中的成员变量x将引用临时对象的成员变量x_。当你稍后使用现在引用被破坏对象中的数据的x时,这当然会导致未定义的行为

有两种方法可以解决这个问题:

  1. 使用emplace_back代替push_back
  2. 添加一个复制构造函数,使x引用对象所拥有的x_变量。

答案 2 :(得分:1)

当您将对象添加到vector时,它会被复制,调用该类&#39;复制构造函数。因为myClass有一个引用成员,所以默认的复制构造函数不适用于你想要的语义,所以你需要定义自己的语义:

myClass (const myClass& rhs) : x(x_) {
    init(rhs.x);
}

当你正在做它时,你可能也想要定义一个赋值运算符:

myClass& operator= (const myClass& rhs) {
    x_ = rhs.x;
    return *this;
}

答案 3 :(得分:0)

通过修复myClass的定义可以很容易地修复它,因此:

class myClass {
private:

public:
    const int x;

    myClass(int x_) : x(x_) {
    };
};

理性:

myClass实现了数据接口的习惯用法,但是如果你打算通过公开实际的数据而不是对它的引用来公开数据。

请记住,引用实际上是一个指针,因此它包含一个内存地址,并且它在你的类中的存在会阻止自动生成一个移动构造函数。