1.程序员角度优化
优化前:
#include <iostream>
#include <time.h>
using namespace std;
class A
{
public:
int val1;
int val2;
public:
A(int v1=0,int v2=0)
:val1(v1),val2(v2)
{
cout << "构造函数被调用" << endl;
cout << "val1 = " << val1 << endl;
cout << "val2 = " << val2 << endl;
}
A(const A& a) //拷贝构造函数
:val1(a.val1), val2(a.val2)
{
cout << "拷贝构造函数被调用" << endl;
}
virtual ~A()
{
cout << "析构函数被调用" << endl;
}
};
A Double(const A& a)
{
A a0; //消耗一个构造函数,消耗一个析构函数
a0.val1 = a.val1;
a0.val2 = a.val2;
return a0; //消耗一个构造函数,消耗一个析构函数
}
int main()
{
A a1(10, 20);
Double(a1);
}
Visual Studio运行结果:
解释:
1)声明对象a1并调用有参构造函数初始化
2)调用Double函数,Double函数内声明局部对象a0并进行缺省构造,默认初始化
3)Double函数中,执行return语句时调用拷贝构造函数拷贝局部对象a0给临时对象
4)Double函数执行完毕,析构局部对象a0
5)Double函数返回的临时对象无变量接收,所以此时临时对象会立即调用析构函数;而当有变量接收的时候,就不会立即析构临时对象
6)main函数执行完,析构局部对象a1
优化后:
#include <iostream>
#include <time.h>
using namespace std;
class A
{
public:
int val1;
int val2;
public:
A(int v1=0,int v2=0)
:val1(v1),val2(v2)
{
cout << "构造函数被调用" << endl;
cout << "val1 = " << val1 << endl;
cout << "val2 = " << val2 << endl;
}
A(const A& a) //拷贝构造函数
:val1(a.val1), val2(a.val2)
{
cout << "拷贝构造函数被调用" << endl;
}
virtual ~A()
{
cout << "析构函数被调用" << endl;
}
};
A Double(const A& a)
{
return A(a.val1*2,a.val2*2); //生成一个临时对象
}
int main()
{
A a1(10, 20);
Double(a0);
}
运行结果:
解释:节省了原来Double函数中局部对象的拷贝构造函数和析构函数的调用
编译器角度代码
//编译器视角Double函数
void Double(A& temp,const A& a)
{
temp.A::A(a.val1*2,a.val2*2);
return;
}
A a1;
a1.A::A(10,20);
A tempObj;
Double(tempObj,a1)
2.编译器角度优化
#include <iostream>
#include <time.h>
using namespace std;
class A
{
public:
int val1;
int val2;
public:
A(int v1=0,int v2=0)
:val1(v1),val2(v2)
{
cout << "构造函数被调用" << endl;
cout << "val1 = " << val1 << endl;
cout << "val2 = " << val2 << endl;
}
A(const A& a) //拷贝构造函数
:val1(a.val1), val2(a.val2)
{
cout << "拷贝构造函数被调用" << endl;
}
virtual ~A()
{
cout << "析构函数被调用" << endl;
}
};
A Double(const A& a)
{
A a0; //消耗一个构造函数,消耗一个析构函数
a0.val1 = a.val1;
a0.val2 = a.val2;
return a0; //消耗一个构造函数,消耗一个析构函数
}
int main()
{
A a1(10, 20);
Double(a1);
}
Linux下GNU的g++编译器运行结果:
解释:
可以发现此结果与上述程序员角度优化后的结果类似,其实这是g++编译器帮我们优化后的结果,g++针对这种返回临时对象的情况,进行了NRV优化(Named Return Value)
我们也可以使用下述命令不让g++编译器为我们优化:
3种写法的速度对比:
#include <iostream>
#include <time.h>
using namespace std;
class A
{
public:
int val1;
int val2;
public:
A(int v1=0,int v2=0)
:val1(v1),val2(v2)
{
cout << "构造函数被调用" << endl;
cout << "val1 = " << val1 << endl;
cout << "val2 = " << val2 << endl;
}
A(const A& a) //拷贝构造函数
:val1(a.val1), val2(a.val2)
{
cout << "拷贝构造函数被调用" << endl;
}
virtual ~A()
{
cout << "析构函数被调用" << endl;
}
};
//第一种:不优化的情况
A Double(const A& a)
{
A a0; //消耗一个构造函数,消耗一个析构函数
a0.val1 = a.val1;
a0.val2 = a.val2;
return a0; //消耗一个构造函数,消耗一个析构函数
}
int main()
{
A a1(10, 20);
clock_t start, end;
start = clock(); //程序开始到本行执行时所用的毫秒数
cout << "start = " << start << endl;
for (int i = 0; i < 1000000; i++)
{
Double(a1);
}
end = clock();
cout << "end = " << end << endl;
cout << end - start << endl;
}
//第二种:程序员角度优化
A Double(const A& a)
{
return A(a.val1 * 2, a.val2 * 2); //生成一个临时对象
}
int main()
{
A a1(10, 20);
clock_t start, end;
start = clock(); //程序开始到本行执行时所用的毫秒数
cout << "start = " << start << endl;
for (int i = 0; i < 1000000; i++)
{
Double(a1);
}
end = clock();
cout << "end = " << end << endl;
cout << end - start << endl;
}
第一种情况:在180ms左右
第二种情况:在110ms左右
第三种情况:在80ms左右
优化说明:
(1)编译器是否真优化了,不好说,你要做各种测试才知道;
(2)如果你的代码很复杂,编译器可能放弃不优化;
(3)不要过度优化,进行一些常见优化,一些高度优化的选项可能使程序出现错误
(4)编译器优化可能不是你想要的,比如你想通过拷贝函数执行一些代码,编译器却帮你进行了优化,不会执行拷贝构造函数
3.程序优化续
#include <iostream>
#include <time.h>
using namespace std;
class A
{
public:
int val;
public:
A()
{
val = 0;
cout << "默认构造函数被调用" << endl;
}
A(const A& a) //拷贝构造函数
{
val = a.val;
cout << "拷贝构造函数被调用" << endl;
}
virtual ~A()
{
cout << "析构函数被调用" << endl;
}
A(int v) //带有一个形参的构造函数,类型转换构造函数
:val(v)
{
cout << "A(int)构造函数被调用" << endl;
}
};
int main()
{
cout << "----------begin-------------" << endl;
A a1(1000);
cout << "----------------------------" << endl;
A a2 = 1000;
cout << "----------------------------" << endl;
A a3 = A(1000);
cout << "----------------------------" << endl;
A a4 =(A)1000;
cout << "----------end---------------" << endl;
}
Visual Studio下运行结果:
Linux下不让编译器优化的运行结果:
解释:
在Visual Studio的编译器中可能进行了优化
而在Linux中未进行优化的情况解释:
(1)A a1(1000);
直接使用调用有参构造函数
//编译器中理解
A a1;
a1.A::A(1000);
(2)A a2 = 1000;
此处进行了隐式类型转换
先生成一个A的临时对象,然后赋值给a2
//编译器中理解
A a;
a.A::A(1000); //A(int)构造函数被调用
A a2;
a2.A::A(a); //拷贝构造函数被调用
a.A::~A(); //析构函数被调用
(3)A a3 = A(1000);
生成一个临时对象,并将它赋值给a3
编译器中理解同(2)
(4)A a4 = (A)1000;
强制类型转换
编译器中理解同 (2)
结论:当编译器面临用一个类对象作为另外一个类对象初值的情况,各个编译器表现不同。但是所有编译器都为了提高效率而努力。我们也没有办法确定我们自己使用的编译器是否一定会调用拷贝构造函数。