传递对C ++构造函数的引用

时间:2013-02-06 13:22:33

标签: c++ reference

我有this code

#include <iostream>
using namespace std;

struct X {
    int a = 1;
};

struct Y {
    X &_x;
    Y(X &x) : _x(x) {}
};

// intentionally typoed version of Y, without the reference in the constructor
struct Z {
    X &_x;
    Z(X x) : _x(x) {}
};

int main() {
    X x;
    Y y(x);
    Z z(x);
    cout << "x:   " << &x << endl;
    cout << "y.x: " << &y._x << endl;
    cout << "z.x: " << &z._x << endl;
}

我一直发现自己忘记了这种格式的类的构造函数中的&

这输出以下内容:

x:   0xbfa195f8
y.x: 0xbfa195f8
z.x: 0xbfa195fc

为什么yz的行为会有所不同?

为什么在X &_x的构造函数中使用X类型的实例初始化Y成员并不是错误?

3 个答案:

答案 0 :(得分:4)

您的代码接近于具有未定义的行为:您绑定对x的构造函数返回时超出范围的对象(Z)的引用,这使其成为悬空引用。您不会尝试在以后取消引用它,这就是UB不显示的原因。但是试着读出z.x的价值,而不是取其地址,例如UB。

分配引用不是错误的原因是允许左值引用绑定到左值,x是左值。在 _x的构造函数返回后,编译器无需确定是否要访问Z甚至(事实上,我担心这在一般情况下是不可能的情况)。

但是,当您尝试将引用绑定到本地对象时,一个不错的编译器至少应该发出警告:尝试使用/Wall选项进行编译,您应该得到一个。

关于y.xz.x之间输出的差异:嗯,您正在打印这些变量的地址,而不是它们的值,而引用只是它们引用的变量的别名。因此,获取引用的地址会产生与获取它所绑定的变量的地址相同的结果。

对于z,引用z.x未绑定到您在x中声明的变量main()(与y.x不同),但是(实际上)绑定到一个超出范围的对象并保持Z的构造函数的参数(通过值传递创建参数的副本,并{{ 1}}的引用是该副本的别名)。因此,z运算符返回不同的地址。

答案 1 :(得分:3)

  

为什么y和z的情况下行为不同?

由于Y通过引用获取参数,Z不会。 Y对原始对象进行操作,Z在副本上运行。

  

为什么在Y的构造函数中使用类型X的实例初始化X&amp; _x成员并不是错误?

诊断不是强制性的。超出构造函数的范围,引用Z::_x无效。因此访问它是无效的(但是,在构造函数中访问_x是可以的)。

答案 2 :(得分:2)

在调用构造函数时会创建对象的副本,因此z.x的输出不同。该对象的生命周期非常有限 - 它仅存在于构造函数中。这是未定义的行为,引用将无效。

为防止应用程序中出现此类行为,最好将复制构造函数和赋值运算符标记为私有。