2

我有这个代码

#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

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

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

4

3 回答 3

4

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

分配引用不会出错的原因是左值引用被允许绑定到左值,并且x是左值。_x即使 ' 的构造函数返回之后,编译器也不需要确定是否要访问Z(实际上,在一般情况下,这恐怕是不可能的)。

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

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

在 的情况下z,引用z.x不绑定到您在(不同于)x中声明的变量,而是(实际上)绑定到已超出范围并持有 的构造函数的参数的对象(按值传递创建参数的副本,并且' 的引用是该副本的别名)。因此,操作员返回不同的地址。main()y.xZz&

于 2013-02-06T13:24:52.920 回答
3

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

因为Y通过引用获取参数,Z所以没有。Y对原始对象Z进行操作,对副本进行操作。

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

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

于 2013-02-06T13:24:55.160 回答
2

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

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

于 2013-02-06T13:24:14.253 回答