【问题标题】:Vector elements in c++ always set to last elementc++中的向量元素总是设置为最后一个元素
【发布时间】:2015-07-16 08:00:12
【问题描述】:

我想将一个类的成员存储到一个向量中。在我的课堂上,我有一些私有变量,我通过常量指针访问它们,以防止它们被更改 (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;
}

Ovious 问题:我做错了什么,我可以解决它吗?

【问题讨论】:

  • 我的直觉是它与你创建的对象被复制有关,成员变量x将引用由例如创建的临时对象的x_成员变量。 myClass(j)。换句话说,尝试创建一个复制构造函数,以确保 x 变量引用自己的 x_ 变量。
  • 你可以发布输出吗?

标签: c++ vector


【解决方案1】:

我做错了什么,我可以解决它吗?

这个想法总体上看起来是错误的。抛开这一点:如果你想“修复它”,你需要考虑当你push_back时发生了什么:

for (int j=0; j<3; j++) {
    myVector.push_back(myClass(j));
}

您正在按值传递类,因此调用了复制构造函数。因此 push_back 没有得到myClass(j),它得到了一个副本。但是您没有编写复制构造函数……那么您的复制构造函数来自哪里?你得到了编译器的默认值,它只是说副本的 int &amp;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 &amp;链接到int。如果你不告诉它,它根本无法知道它们是相关的。

但首先不做这种界面的原因可能不止几个。只需使用普通的访问器方法,除非您有非常好的理由不这样做。

(还要注意@JoachimPileborg 对 emplace_back 的建议,这很值得了解。)

【讨论】:

  • 我想没有办法以某种方式将其简化为单个构造函数方法?有了这个,我必须为我班上的每个变量做两次 x(x_)。
  • @DaPhil 有一种“现代 C++”理想化,大部分时间你不编写复制构造函数、移动构造函数,甚至析构函数......你让编译器为你做这一切。 (智能指针等有助于实现这一目标)。但是在你的方法中,如果你发现你有多个这些变量,那么你可以制作一种包含它们的包装器结构,对其进行模板化,然后为它做一个引用初始化。参见例如QSharedDataPointer
【解决方案2】:

扩展我的评论...

当你这样做时

myVector.push_back(myClass(0));

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

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

  1. 使用emplace_back 而不是push_back
  2. 添加一个复制构造函数,使x 引用对象自己的x_ 变量。

【讨论】:

  • 我尝试了 emplace_back,因为这似乎是更容易的尝试。但它给了我与 push_back 相同的输出。无论如何感谢您的帮助。
【解决方案3】:

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

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

当您使用它时,您可能还想定义一个赋值运算符:

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

【讨论】:

    【解决方案4】:

    通过修复myClass的定义很容易解决,因此:

    class myClass {
    private:
    
    public:
        const int x;
    
        myClass(int x_) : x(x_) {
        };
    };
    

    理性:

    myClass 实现了 data-is-interface 习惯用法,这很好,但如果您要公开数据,请通过公开实际数据来实现 - 而不是对它的引用。

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

    【讨论】:

      猜你喜欢
      • 2014-05-22
      • 2012-12-25
      • 1970-01-01
      • 2019-05-16
      • 1970-01-01
      • 2012-08-20
      • 2015-01-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多