传递对C++构造函数的引用
Passing a reference to a C++ constructor
我有这个代码:
#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
的行为不同?
为什么在Y
的构造函数中用类型为X
的实例初始化X &_x
成员不是错误?
您的代码接近于具有Undefined Behavior:当Z
的构造函数返回时,您正在将引用绑定到超出范围的对象(x
),这使它成为一个悬空引用。您以后不会试图取消引用它,这就是UB没有出现的原因。但是,尝试读出z.x
的值,而不是取其地址,例如,将是UB。
分配引用不是错误的原因是允许左值引用绑定到左值,并且x
是左值。编译器不需要确定是否要访问_x
,即使在Z
的构造函数返回后也是如此(事实上,恐怕在一般情况下这是不可能的)。
然而,当您尝试将引用绑定到本地对象时,一个好的编译器至少应该发出警告:尝试使用/Wall
选项进行编译,您应该得到一个。
关于y.x
和z.x
之间的输出差异:好吧,您打印的是这些变量的地址,而不是它们的值,引用只是它们引用的变量的别名。因此,获取引用的地址与获取其绑定的变量的地址产生相同的结果
在z
的情况下,引用z.x
没有绑定到您在main()
中声明的变量x
(与y.x
不同),而是(实际上)绑定到一个超出范围并包含Z
构造函数参数的对象(传递值会创建参数的副本,而z
的引用是该副本的别名)。因此,&
运算符返回不同的地址。
为什么y和z的行为不同?
因为Y
引用了该参数,所以Z
没有。Y
对原始对象进行操作,Z
对副本进行操作。
为什么初始化X&_在Y的构造函数中具有x类型实例的x成员?
诊断不是强制性的。在构造函数的作用域之外,引用Z::_x
是无效的。因此访问它是无效的(但是,在构造函数内访问_x
是可以的)。
调用构造函数时会创建对象的副本,因此z.x的输出不同。该对象的寿命非常有限,它只存在于构造函数中。这是未定义的行为,引用将无效。
为了防止应用程序中出现这种行为,最好将复制构造函数和赋值运算符标记为私有。
- 在类构造函数中传递对外部函数的引用
- 在 c++ 中将变量作为结构构造函数中的引用传递
- C++ 尝试在不存在的构造函数中引用已删除的函数(使用 rapidJson)
- 移动构造函数和右值引用
- 为什么我的运算符 + 重载尽管是通过引用传递的,但仍调用我的复制构造函数?
- 如何使用 swig 修改类构造函数以保留对其中一个构造函数参数的引用?
- 在引用初始化中使用已删除的复制构造函数进行复制初始化
- C++:右值引用构造函数和复制省略
- 当有右值构造函数可用时,为什么从右值调用类引用构造函数重载?
- 引用构造函数时链接失败
- 为什么调用转发引用构造函数而不是复制构造函数?
- 为什么使用已删除的右值引用构造函数?
- 对 std::Optional 的转发引用构造函数的约束
- 模板是否应该为不同类型的参数制作非 Rvalue 引用构造函数/赋值
- 我是否应该对非引用构造函数参数手动调用move
- 使用左值引用错误地调用了Rvalue引用构造函数
- C++未定义的引用构造函数错误
- 调用左值引用构造函数而不是右值引用构造函数
- C++引用构造函数语法
- 不能引用构造函数的地址