编译器优化问题:
不同编译器优化是不一样的,下面代码我都用的vs2019,并且在Debud模式下。(Release也会进行优化)
下面测试的时候我先采用Debug模式测试。
先写一个简单的类,进行打印测试:
class A
{
public:
A()
{
cout << "A()" << endl;
}
A(const A& a)
{
cout << "A(const A& a)" << endl;
}
A& operator=(const A& a)
{
cout << "A& operator=(const A& a)" << endl;
return *this;
}
~A()
{
cout << "~A()" << endl;
}
};
下面代码运行的话,会打印几次构造?几次拷贝?
void f1(A a)
{
}
int main()
{
A a1;
f1(a1);
return 0;
}
首先 a1 肯定构造这是一次构造没问题,那么 a1 传给 f1() 是什么呢?
这里 a1 传给 f1 的时候发生了拷贝,不是 a1 直接给 a,而是中间产生了一个临时变量 a1 先拷贝给临时变量,然后临时变量再传给了 a,下面画图解释:
所以打印出来是 1次构造+1次拷贝。
那么下面这个代码呢?几次构造,几次拷贝?
void f2(A& a)
{
}
int main()
{
A a1;
f2(a1);
return 0;
}
注意看这里的 f2() 函数里面的形参我加了引用!再结合前面我发的引用结合一下,所以这里应该是几次构造?几次拷贝?
答案:1次构造+0次拷贝,打印看一下:
那么为什么呢?结合前面写的关于引用的文章,引用做参数可以减少拷贝,提高效率。
增加难度,下面结合匿名对象来看编译器是如何优化的。
下面代码运行之后几次构造,几次拷贝构造?
void f1(A a)
{
}
int main()
{
f1(A());
return 0;
}
这里运行之后是 1次构造+0次拷贝构造,运行结果如下:
但是,那么不优化是什么呢?
不优化的话是 1次构造+1次拷贝。优化因为构造好了之后,再传给 f1 的时候,发生了优化,编译器直接合二为一,优化掉了拷贝。
结论:连续一个表达式步骤中,连续构造一般都会优化,合二为一。(这里还牵扯栈帧方面的知识)
那么再继续,下面代码几次构造,几次拷贝?
A f3()
{
A ret;
return ret;
}
int main()
{
f3();
return 0;
}
答案:1次构造+1次拷贝。
首先 f3 函数内部 A 先构造了一个 ret 对象没问题吧,那么拷贝哪里来的?还是一样结合前面的,这里返回是传值返回,返回的时候先拷贝给临时对象,然后这个临时对象在返回给调用 f3 的地方,所以发生了拷贝。
那么为什么不用引用返回减少拷贝?
这里的 ret 是局部对象,出了 f3 的域就销毁了,你再想通过引用去找那么访问的就是随机值。
那么继续,下面代码几次构造,几次拷贝?
A f3()
{
A ret;
return ret;
}
int main()
{
A a = f3();
return 0;
}
答案:优化之后: 1次构造+1次拷贝,那么不优化呢?
不优化是1次构造+2次拷贝,为什么呢?
结合上面的例子,这里两次拷贝应该不难,优化之后,编译器就好比直接通过拷贝一次临时对象传给了a(这里牵扯栈帧的知识)
下面难道再提升,下面代码几次构造,几次拷贝?
A f4(A a)
{
A a1(a);
A a2(a1);
return a2;
}
int main()
{
A aa;
A aa1 = f4(aa);
return 0;
}
答案:优化之后,1次构造+4次拷贝;不优化是1次构造+5次拷贝,原因如下:
绿色的线是优化之后的,蓝色是没优化的时候,这里编译器优化处理了,通过一次临时对象拷贝给了 aa1。
那么继续,下面代码几次构造,几次拷贝?
A f4(A a)
{
A a1(a);
A a2(a1);
return a2;
}
int main()
{
A aa;
A aa1 = f4(f4(aa));
return 0;
}
答案:优化之后是1次构造+7次拷贝;不优化是1次构造+9次拷贝。原因如下:
优化之后的:
不优化之后的:
这个就是编译器优化之后和优化之前的对比,编译器优化依不同编译器不同不一样,linux环境下也是不一样,linux可以加参数取消优化,加的参数是 -fno-elide-constructors 这个参数加一下可以运行看一下结果,我就举最后一个例子试一下,如下:
运行结果如下:1次构造+9次拷贝
以上就是编译器优化的例子,根据不同场景不同编译器优化也是不一样的。