C ++ 11引用类型的成员变量,矢量push_back后的不同行为

时间:2015-01-23 11:37:54

标签: c++ c++11 vector reference member

当我把它推入矢量时,我正在使用其他人的类,这个类很奇怪。它涉及一个成员变量,它是对另一个成员变量的引用。这是最小的自包含示例:

#include <iostream>
#include <vector>

class Myclass {
public: 
  Myclass() : a(1.0) {}

  float a;
  float &a_ref = a;

  void addOne() {
    a = a + 1.0;
  }
};

int main() {
  Myclass instance1;
  instance1.addOne();

  //prints 2:
  std::cout << "instance.a_ref is " << instance1.a_ref << std::endl;

  std::vector<Myclass> vec;
  Myclass instance2;
  vec.push_back(instance2);

  vec.at(0).addOne();

  //prints 1;
  std::cout << "vec.at(0).a_ref is " << vec.at(0).a_ref << std::endl;
  return 0;
}

我正在使用g++-std=c++11进行编译,所以我暂时没有注意到这个问题。我现在看到问题可能与合成的复制构造函数和引用成员有关。但我不确定的是:

  1. 当对象在向量中时,为什么会有不同的行为?
  2. 为什么g++没有使用c ++ 11标准给出任何警告?
  3. 奖金问题,因为我很好奇:

    1. 首先进行了哪些初始化,aa_ref

2 个答案:

答案 0 :(得分:16)

问题确实存在于默认的拷贝构造函数中。默认的复制构造函数初始化源对象成员中的所有成员。也就是说,默认的拷贝构造函数与此相同:

Myclass(const Myclass &src) :
  a(src.a),
  a_ref(src.a_ref)
{}

默认的拷贝构造函数初始化所有成员,因此忽略任何类内的初始化者。

这也是推入向量导致问题的原因。 vec.at(0)被创建为instance2的副本,这意味着vec.at(0).a_ref指的是instance2.a。您可以通过打印地址(live example)轻松验证这一点。

答案 1 :(得分:7)

隐式定义的复制/移动构造函数:

  

[...]执行其基础和成员的成员复制/移动。 [注意:忽略非静态数据成员的 brace-or-equal-initializers 。 [...]

特别是,引用成员是直接初始化的,以引用源对象中相应引用成员引用的同一对象。

因此,在您的情况下,vec.at(0).a_ref是指a的成员instance2

编译器未检测到这种情况,因为通常引用成员应引用类外的较长寿命对象。