You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 04:35:24