July.cc 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 日