【问题标题】:Passing a reference to a C++ constructor传递对 C++ 构造函数的引用
【发布时间】:2013-02-06 13:22:32
【问题描述】:

我有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;
}

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

这会输出以下内容:

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

为什么yz 的行为不同?

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

【问题讨论】:

    标签: c++ reference


    【解决方案1】:

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

    赋值引用不会出错的原因是允许左值引用绑定到左值,而x是左值。编译器不需要判断你是否要访问_x,即使之后 Z的构造函数返回(其实一般情况下这恐怕是不可能的)。

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

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

    z 的情况下,引用z.x 未绑定到您在main() 中声明的变量x(与y.x 不同),而是(实际上)绑定到一个对象它已经超出范围并持有Z 的构造函数的参数(按值传递会创建参数的副本z 的引用是该副本的别名)。因此,&amp; 运算符返回一个不同的地址。

    【讨论】:

    • 实际上,在他开始使用 Z::_x 之前,代码没有未定义的行为,而他在代码中从未这样做过。
    • @thang: cout &lt;&lt; "z.x: " &lt;&lt; &amp;z._x &lt;&lt; endl;
    • 这并没有错。如果用 z._x.a 代替 &z._x,那就另当别论了。
    • @thang: 取消引用悬空引用没有什么问题吗?
    • @thang:好吧,他正在尝试读取引用对象的值
    【解决方案2】:

    为什么y和z的行为不同?

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

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

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

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

      • 是的,添加 X(const X&amp; that) = delete; 效果很好 - 谢谢!
      猜你喜欢
      • 2016-02-15
      • 1970-01-01
      • 1970-01-01
      • 2017-09-11
      • 2011-10-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-19
      相关资源
      最近更新 更多