为什么需要(对象)优化
我们都知道,C语言和C++在程序执行中,都是通过调用一系列的函数来实现的。并且,很多时候,编译器会帮助我们做一系列的事情,比如(在编译类的成员方法的时候,编译器默认添加 this 指针,以此来确定是哪一个对象调用了该成员方法)。
得益于编译器或者说系统帮助我们做的一系列事情,我们可以更加方便地使用C++。
但是凡事有利必有弊,因为系统有时候会自己调用一系列的函数,从另一个角度来说,也一定程度上降低了效率。
而我们想要提高C++的执行效率,就需要了解程序(主要是对象)使用过程中都调用了哪些方法。
对象使用过程中都调用了哪些方法
示例代码:
我们首先来看这么一段代码:
#include <iostream>
using namespace std;
class Test
{
public:
Test(int a = 10)
:ma(a)
{
cout << "Test(int)" << endl;
}
~Test()
{
cout << "~Test()" << endl;
}
Test(const Test& t)
:ma(t.ma)
{
cout << "Test(const Test&)" << endl;
}
Test& operator=(const Test& t)
{
cout << "operator=" << endl;
ma = t.ma;
return *this;
}
int getdata()const {
return ma; }
private:
int ma;
};
Test GetObject(Test t)
{
int val = t.getdata();
Test tmp(val);
return tmp;
}
int main()
{
Test t1;
Test t2;
t2 = GetObject(t1);
return 0;
}
运行结果:
结果解释:
我们可以发现,代码中简单的几行,为什么运行结果会有这么多行呢?
实际上,这就是问题所在了。
详细地:
- 第一步,主函数中调用构造函数构造
t1
对象; - 第二步,主函数中调用构造函数构造
t2
对象; - 第三步,参数传递过程中,对于对象而言发生初始化而非赋值,所以调用了拷贝构造函数构造形参对象
t
; - 第四步,在
GetObject()
函数中调用构造函数构造对象tmp
; - 第五步,局部对象
tmp
不能带出函数的作用域,所以,要想把tmp
的数值带出来,就需要在主函数的栈帧上
用tmp
拷贝构造一个临时对象; - 第六步,局部对象
tmp
析构; - 第七步,局部形参对象
t
析构; - 第八步,主函数
main()
栈帧上的临时对象赋值给t2
; - 第九步,主函数栈帧上的临时对象析构。
- 第十步,
t2
析构; - 第十一步,
t1
析构。
对象优化及规则
优化后的结果
这是优化后的结果,我们可以发现:
实现了同样的功能代码;
优化前,调用了11
条语句;
优化后,调用了4
条语句;
代码执行的效率得到了大大的提高!
具体优化过程
一、我们可以做如下改动:
原来的:Test GetObject(Test t)
优化后:Test GetObject(Test& t)
做这一步的目的是,减少参数传递过程中的拷贝构造,并且,也少了形参对象的析构,简而言之,少了两条调用语句:
二、继续优化如下:
原来的:Test tmp(val);return tmp;
优化后:return Test(val);
这一步的目的是,减少局部变量tmp
的构造,以及tmp
的析构。
我们可以这么理解:
直接用临时对象取代原来定义好的局部对象,
之后用临时对象来构造一个主函数上的新对象;
这个时候编译器就会优化,不生成临时对象;
直接构造新对象。
三、继续优化如下:
原来的:Test t2;t2 = GetObject(t1);
优化后:Test t2 = GetObject(t1);
这一步的目的是,省去主函数栈帧上的临时对象的构造和析构,并且省去了赋值函数。
也就是说,这一步优化,省去了3步
!
对象优化的三条规则
根据上述过程,我们可以总结规则如下:
- 函数参数传递过程中,对象优先按引用传递,不要按值传递;
- 函数返回对象的时候,应该优先返回一个临时对象,而不要返回一个定义过的对象;
- 接收返回值是对象的函数调用的时候,优先按初始化的方式接收,不要按赋值的方式接收。
转载:https://blog.csdn.net/m0_46308273/article/details/116996745