关于Move Semantics与Copy-Elision的技术疑问
嘿,我太懂这种看了一堆教程还是摸不着头脑的感觉了!先把你给出的这个Foo类里的几个核心构造函数拆解开,你就能明白它们各自的作用和什么时候会被调用了。
拆解你的
Foo类构造函数 1. 无参构造函数(Empty ctor)
Foo() : i_(0) { std::cout << "Foo Empty ctor" << std::endl; }
这是最基础的构造函数,当你创建一个没有任何初始化值的Foo对象时会触发它,比如:
Foo f; // 这里就会调用无参构造,i_被初始化为0
它的作用是给对象一个默认的初始状态。
2. 拷贝构造函数(Copy ctor)
Foo(const Foo& foo) : i_(foo.i_) { std::cout << "Foo Copy ctor" << std::endl; }
这个构造函数的作用是用一个已经存在的Foo对象,复制出一个完全一样的新对象。参数里的const Foo&是关键:
const保证我们不会修改源对象的状态- 引用
&避免了创建参数时的无限递归(如果不用引用,传参时会调用拷贝构造,又要传参,无限循环)
触发场景比如:
Foo f1; Foo f2(f1); // 直接初始化,调用拷贝构造 Foo f3 = f1; // 拷贝初始化,也会调用拷贝构造
执行后f2和f3的i_值都会和f1一样,而f1本身不会有任何变化。
3. 带参数的构造函数(Param ctor)
Foo(int i) : i_(i) { std::cout << "Foo Param ctor" << std::endl; }
这个构造函数允许你用一个int值直接创建Foo对象,把传入的i赋值给成员变量i_。触发场景很直观:
Foo f(5); // 直接用int值初始化,调用带参构造 Foo f = 10; // 这里会触发隐式转换,先把10转成Foo临时对象,再赋值(不过编译器可能优化成直接调用带参构造)
如果给这个构造函数加上explicit关键字,就会禁止这种隐式转换,Foo f = 10;这种写法就会报错,只能用Foo f(10);。
4. 移动构造函数(Move ctor)
Foo(Foo&& foo) : i_(foo.i_) { foo.i_ = 0; std::cout << "Foo Move ctor" << std::endl; }
这是C++11新增的构造函数,核心作用是**“窃取”一个右值对象的资源**(这里的例子是简单的int,实际场景比如动态分配的内存、文件句柄这类资源),避免不必要的拷贝,提升性能。
什么是右值?简单说就是“临时存在的对象”,比如:
- 直接创建的临时对象:
Foo(20) - 用
std::move()转换后的左值:std::move(f)
触发场景例子:
Foo f1(10); Foo f2 = std::move(f1); // 调用移动构造,f2拿到f1的i_值,f1的i_被设为0 Foo f3 = Foo(20); // 临时对象作为右值,调用移动构造(如果编译器没做返回值优化的话)
注意:移动构造执行后,源对象(比如上面的f1)会处于“有效但未定义”的状态,所以最好不要再使用它,除非你重新给它赋值。例子里把foo.i_ = 0是个好习惯,明确给源对象一个已知的状态。
举个完整的调用例子
#include <iostream> #include <utility> // 用于std::move class Foo { public: Foo() : i_(0) { std::cout << "Foo Empty ctor" << std::endl; } Foo(const Foo& foo) : i_(foo.i_) { std::cout << "Foo Copy ctor" << std::endl; } Foo(int i) : i_(i) { std::cout << "Foo Param ctor" << std::endl; } Foo(Foo&& foo) : i_(foo.i_) { foo.i_ = 0; std::cout << "Foo Move ctor" << std::endl; } private: int i_; }; int main() { std::cout << "创建f1: "; Foo f1; // 无参构造 std::cout << "\n创建f2(拷贝f1): "; Foo f2(f1); // 拷贝构造 std::cout << "\n创建f3(用int值): "; Foo f3(15); // 带参构造 std::cout << "\n创建f4(移动f3): "; Foo f4 = std::move(f3); // 移动构造 std::cout << "\n创建f5(临时对象): "; Foo f5 = Foo(20); // 带参构造+移动构造(或编译器优化为直接带参构造) }
运行后(如果关闭编译器优化,比如-O0)的输出大概是:
创建f1: Foo Empty ctor 创建f2(拷贝f1): Foo Copy ctor 创建f3(用int值): Foo Param ctor 创建f4(移动f3): Foo Move ctor 创建f5(临时对象): Foo Param ctor Foo Move ctor
如果开启优化(比如-O2),最后一行的移动构造会被编译器优化掉,直接输出Foo Param ctor,这就是返回值优化(NRVO)。
内容的提问来源于stack exchange,提问作者Moia
相关产品推荐
相关产品推荐

