传递临时对象初始化引用成员变量的技术疑问
C++临时对象与构造函数相关疑问解答
代码示例
#include <iostream> class B { public: B() { x = 5; std::cout << "B's default constructor called, creating object: " << this << "\n"; } B(const B &other) { std::cout << "B's copy constructor called, copying other object: " << &other << "to this object: " << this << "\n"; } B(B &&other) { std::cout << "B's Move constructor called, moving other object: " << &other << "to this object: " << this << "\n"; } int x; }; class A { public: A(B other_b) : b_(other_b) { std::cout << "A's constructor called, creating object: " << this << ", Address of b recieved is: " << &other_b << ", Address of A's b: " << &b_ << "\n"; } void print() { std::cout << "Address of this object: " << this << ", Address of b: " << &b_ << " " <<b_.x << std::endl; } private: B &b_; }; int main() { A a{B()}; // initializing with temporary a.print(); }
编译输出
g++ -O0 -fno-elide-constructors ref_class_member.cpp // to disable some comiler optimizations B's default constructor called, creating object: 0x7ffebd667d5c A's constructor called, creating object: 0x7ffebd667d50, Address of b recieved is: 0x7ffebd667d5c, Address of A's b: 0x7ffebd667d5c Address of this object: 0x7ffebd667d50, Address of b: 0x7ffebd667d5c 5
技术疑问解答
问题1:当使用临时B对象调用A的构造函数后,该临时对象应已超出作用域被销毁,为何调用a.print()访问b_.x未出现错误?
这属于未定义行为,并非临时对象真的没被销毁。临时对象B()的生命周期在A的构造函数执行完毕后就已结束,对应的栈内存会被标记为可复用。只是此时这块内存还没有被后续操作覆盖,所以访问b_.x时刚好读取到了原来的有效值,但这种情况完全不可靠——只要后续有栈操作(比如其他函数调用)覆盖这块内存,就会触发崩溃或读取到错误值。
另外要注意:A的成员b_是引用类型,它绑定到了构造函数的参数other_b,而other_b是构造函数的局部变量,在构造函数结束后也会被销毁,所以b_本质上是一个悬垂引用,访问悬垂引用的任何成员都是标准明确规定的未定义行为。
问题2:A的构造函数为A(B other_b),按逻辑临时对象应被拷贝,为何未观察到copy constructor的调用?
这是因为C++17及以后引入的强制拷贝省略规则。当用纯右值(比如B()这种临时对象)初始化同类型对象时,编译器会直接在目标对象的内存位置构造这个临时对象,完全跳过拷贝或移动构造函数的调用。
即使你添加了-fno-elide-constructors编译选项,这个规则依然生效——因为这不是可选的编译器优化,而是C++标准强制要求的行为,编译器必须执行这种直接构造,因此不会触发拷贝或移动构造函数,自然看不到对应的打印输出。
内容的提问来源于stack exchange,提问作者photographyraptor
相关产品推荐
相关产品推荐

