humid1ch blogs

本篇文章

手机用户建议
PC模式 或 横屏
阅读


C++ 2023 年 3 月 19 日

[C++] C++编译器 关于构造函数的优化

一些编译器, 针对连续使用的构造函数 会存在一些优化的行为

C++编译器关于构造函数的优化

在本篇文章开始之前, 先来思考一道问题:
Widget 是一个类
那么, 运行这段代码, 拷贝构造函数调用几次?

分析一下:

首先是 f(x):

  1. x传值传参, 一次
  2. u拷贝构造v, 一次
  3. v拷贝构造w, 一次
  4. w传值返回, 一次

一共是 4 次;


然后是 f(f(x)):

  1. f(x)的返回值, 传值传参, 一次
  2. u拷贝构造v, 一次
  3. v拷贝构造w, 一次
  4. w传值返回, 一次
  5. f(f(x))的返回值拷贝构造y, 一次

一共是 5

所以综合应该一共是调用 9 次拷贝构造函数

但是, 验证一下会发现, 结果并不是这样:
为什么呢?
下面来拆分分析一下:
int main() {
	Widget x;
	f(x);
    
	return 0;
}

单独调用: f(x)

  1. x传值传参, 一次
  2. u拷贝构造v, 一次
  3. v拷贝构造w, 一次
  4. w传值返回, 一次

所以一共是 4 次拷贝构造:

继续分析之前, 先补充一个内容: 匿名对象

什么是匿名对象? 以上面的类为例: Widget() 就是一个匿名对象的实例化

匿名对象的生命周期, 仅在其定义的一行之内 (被取别名当然会延长):

如果使用 匿名对象 对 函数f 传参, 拷贝构造函数会调用多少次呢?

f(Widget());

分析:

  1. Widget() 传值传参, 一次
  2. u拷贝构造v, 一次
  3. v拷贝构造w, 一次
  4. w传值返回, 一次

所以应该是 4

但是可以看到, 答案是 3 次, 这又是为什么呢?

原因是: 编译器将一个连续的过程—— 构造 + 拷贝构造 优化了

f(Widget()); 语句中, Widget()的构造 与 构造完成后的传值传参, 就是一个 构造 + 拷贝构造 的连续过程 而这个过程会被一些编译器(并不是所有的编译器) 优化为 只有、直接构造


而 同样会被优化的 还有 拷贝构造 + 拷贝构造 这个连续过程: 比如: Widget w = f(x); 这个语句

按照不优化的分析:

  1. x 传值传参, 一次
  2. u拷贝构造v, 一次
  3. v拷贝构造w, 一次
  4. w传值返回, 一次
  5. f(x) 返回值 拷贝构造 w, 一次

一共应该是 5

但是结果是 4

原因是: w 传值返回 和 f(x)的返回值 拷贝构造 w, 是一个连续的 拷贝构造 + 拷贝构造 的过程

会被合并为 一个拷贝构造

如果是 w = f(x) 还有一个赋值的过程, 在 Widget w = f(x) 中被优化了

那么 Widget w = f(f(x)); 应该怎么分析呢?

  1. x传值传参, 一次
  2. u拷贝构造v, 一次
  3. v拷贝构造w, 一次
  4. w传值返回, 得到f(x), 一次
  5. f(x)返回值作为参数, 传值传参, 一次
  6. u拷贝构造v, 一次
  7. v拷贝构造w, 一次
  8. w传值返回, 得到f(f(x)), 一次
  9. f(f(x))返回值 拷贝构造 w, 一次

不优化应该调用 9 次拷贝构造

但是, 一些编译器对步骤 4、5, 步骤 8、9, 会进行优化使其合二为一

所以优化过后应该是调用 7 次拷贝构造:

版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)

作者: 哈米d1ch 发表日期:2023 年 3 月 19 日