知识的积累在于日积月累,每天掌握一点点,你会变得更加强大。
关于如何分析对象的生命周期,可以参照博文(https://mp.youkuaiyun.com/postedit/89514602)。本片文章主要针对如何对程序进行优化,减少一些无关函数的调用,以提高程序的执行效率。
我们首先给出一段程序:
#include<iostream>
using namespace std;
class Test
{
public:
Test(int data = 100):ma(data) //构造函数
{cout<<"Test(int)"<<endl;}
~Test() //析构函数
{cout<<"~Test()"<<endl;}
Test(const Test &src):ma(src.ma) //拷贝构造函数
{cout<<"Test(const Test&)"<<endl;}
Test&operator=(const Test &src) //赋值运算符重载函数
{
cout<<"operator="<<endl;
ma = src.ma;
return *this;
}
int getdata()
{return ma;}
private:
int ma;
};
Test GetTestObject(Test t)
{
int value = t.getdata();
Test tmp(value);
return tmp;
}
int main()
{
Test t1;
Test t2;
t2 = GetTestObject(t1);
cout<<t2.getdata()<<endl;
return 0;
}
执行结果:
Test(int) //普通构造t1
Test(int) //普通构造t2
Test(const Test&) //传参的过程,相当于“t = t1”,调用拷贝构造
Test(int) //普通构造tmp
Test(const Test&) //函数返回时,相当于“t2 = tmp”,调用拷贝构造
~Test() //析构tmp
~Test() //析构t
operator= //临时对象赋值t2
~Test() //析构临时对象
100
~Test() //析构t2
~Test() //析构t1
//上述两处拷贝构造函数并非真正意义上的拷贝构造过程,具体将在后面进行说明
优化原则一:
函数调用需要传入对象时,应该按照按对象引用来传递。我们知道引用实际上就是别名,使用引用,其实就是在使用自己,这样会减少两个函数的调用开销。(拷贝构造函数和析构函数)
我们对上面代码做出修改,并输出执行结果:
#include<iostream>
using namespace std;
class Test
{
public:
Test(int data = 100):ma(data) //构造函数
{cout<<"Test(int)"<<endl;}
~Test() //析构函数
{cout<<"~Test()"<<endl;}
Test(const Test &src):ma(src.ma) //拷贝构造函数
{cout<<"Test(const Test&)"<<endl;}
Test&operator=(const Test &src) //赋值运算符重载函数
{
cout<<"operator="<<endl;
ma = src.ma;
return *this;
}
int getdata()
{return ma;}
private:
int ma;
};
Test GetTestObject(Test &t)
{
int value = t.getdata();
Test tmp(value);
return tmp;
}
int main()
{
Test t1;
Test t2;
t2 = GetTestObject(t1);
cout<<t2.getdata()<<endl;
return 0;
}
执行结果(这里不再进行详细阐述):
优化原则二:
函数返回值为对象的时候,应该返回一个临时对象,不要先定义对象,再返回,直接返回临时对象,临时对象的形式为:类型名(value);我们知道当返回值是临时对象时,编译系统会生成临时对象,由返回对象拷贝构造临时对象,当函数结束时,返回对象出作用域时自动调用析构函数。然后临时对象赋值给定义,临时对象自动调用析构函数释放自己。而当我们返回一个临时对象时,编译器会直接普通构造定义的对象。减少了函数的调用过程。
我们对上面代码做出修改,并输出执行结果:
#include<iostream>
using namespace std;
class Test
{
public:
Test(int data = 100):ma(data) //构造函数
{
cout<<"Test(int)"<< endl;
}
~Test() //析构函数
{cout<<"~Test()"<<endl;}
Test(const Test &src):ma(src.ma) //拷贝构造函数
{cout<<"Test(const Test&)"<<endl;}
Test&operator=(const Test &src) //赋值运算符重载函数
{
cout<<"operator="<<endl;
ma = src.ma;
return *this;
}
int getdata()
{return ma;}
private:
int ma;
};
Test GetTestObject(Test &t)
{
int value = t.getdata();
//Test tmp(value);
return Test(value);
}
int main()
{
Test t1(5);
Test t2(8);
t2 = GetTestObject(t1);
cout<<t2.getdata()<<endl;
return 0;
}
执行结果(这里不再进行详细阐述):
优化原则三:
调用返回对象的函数时,应该以初始化的方式调用,不要以赋值的方式调用。我们知道对于执行语句“t2 = GetTestObject(t1);临时对象会调用赋值函数对t2进行赋值并且临时对象会调用析构函数。而如果采用初始化的方式,则直接普通构造定义对象。
我们对上面代码做出修改,并输出执行结果:
#include<iostream>
using namespace std;
class Test
{
public:
Test(int data = 100):ma(data) //构造函数
{
cout<<"Test(int)" << endl;
}
~Test() //析构函数
{cout<<"~Test()" << endl;}
Test(const Test &src):ma(src.ma) //拷贝构造函数
{cout<<"Test(const Test&)"<< endl;}
Test&operator=(const Test &src) //赋值运算符重载函数
{
cout<<"operator=" <<endl;
ma = src.ma;
return *this;
}
int getdata()
{return ma;}
private:
int ma;
};
Test GetTestObject(Test &t)
{
int value = t.getdata();
//Test tmp(value);
return Test(value);
}
int main()
{
Test t1;
Test t2 = GetTestObject(t1);
cout<<t2.getdata()<<endl;
return 0;
}
执行结果:
第二个Test(int)直接构造t2,并没有执行临时对象拷贝构造t2的过程
最后我们做出总结:
- 函数调用传对象时,应该按对象引用来传递
- 函数返回对象的时候,应该返回一个临时对象,不要先定义,再返回,应该直接返回临时对象
- 调用返回对象的函数时,应该以初始化的方式调用,不要以赋值的方式调用
- 临时对象拷贝构造对象时,直接普通构造对象