C++基本函數的調用優化(構造、拷貝構造、賦值)
合理的函數可提升時間和空間的利用率
//Test1.h #include<iostream> using namespace std; struct ST { private: int a; short b; public: ST(int a=0, short b=0):a(a),b(b) { this->a = a; this->b = b; cout<<"Object was Built. "<<this<<endl; } ~ST() { cout<<"Object was Free. "<<this<<endl; } ST(const ST &t); ST& operator=(const ST &t); int Get_a(); }; ST::ST(const ST &t) { this->a = t.a; this->b = t.b; cout<<"Object was Copy. "<<this<<endl; } ST& ST::operator=(const ST &t) { cout<<"Assign:"<<this<<" = "<<&t<<endl; if(this != &t) { this->a = t.a; this->b = t.b; } return *this; } int ST::Get_a() { return this->a; }
#include<iostream> #include"Test1.h" using namespace std;
ST fun(ST t)
{
int value = t.Get_a();
ST tmp(value);
return tmp;
} void main() { ST t(12,13); ST t1(t); ST t2; t2 = fun(t); }
運行結果及分析①
1:對象t的構造和析構
2:對象t1的拷貝構造與析構
3:t2的構造與析構
ST fun(ST t) { int value = t.Get_a(); ST tmp(value); return tmp; }
4:fun()函數的參數為對象,調用拷貝構造函數創建臨時對象,其在fun函數結束後被析構掉
5:fun()函數內使用ST實例化類tmp,其在函數結束後被析構掉
6:函數返回時調用拷貝構造函數創建一個臨時對象,該對象在完成賦值後才被析構掉,所以6並不是tmp,這個臨時對象可以生存到函數結束
運行結果及分析②
ST fun(ST t) { int value = t.Get_a(); return ST (value); }
改變的代碼僅有fun()函數變化
1,2,3,4與相同,不再贅述
這裏的改變是不再創造有名對象,而是直接返回一個無名臨時對象,將①的5,6合二為一,所以返回時僅需要使用構造函數構造一個無名臨時對象,
在賦值後被析構掉。省掉了①中的拷貝構造。
運行結果及分析③
ST fun(ST &t) { int value = t.Get_a(); return ST(value); }
與②相比,只改變了參數
1,2,3同上
當使用引用傳遞參數時,就不需要使用拷貝構造函數創建臨時對象,將②的4省掉,③的4與②的4作用相同
運行結果及分析④
ST fun(ST &t)
{
int value = t.Get_a();
return ST(value);
}
void main() { ST t(12,13); ST t1(t); ST t2 = fun(t); }
這裏與③的不同是將主函數修改
主函數內st2從先使用構造函數初始化,再賦值,變成了直接使用賦值為其初始化
在前面我們知道
ST t(1,2);
ST st;
st = t;
和
ST t(1,2);
ST st = t;
實例化st 的不同是 前者先構造再拷貝構造,後者只需要拷貝構造。
所以,主函數使用了上例子後者的寫法,可以直接省去t2的構造,直接拷貝構造,但是關鍵問題就在這裏,
由於fun()函數返回的對象是一個無名的臨時對象,所以編譯器直接讓其初始化t2,而省去拷貝構造的過程。
總結
1.從②我們可以知道,函數使用無名臨時對象作為返回值,比在函數內創建有名臨時對象更快,更節省空間,提升效率。
2.從③我們可以看出來在函數參數為對象時,使用引用可以省掉拷貝構造。
3.從④可以知道,在使用對象對對象進行賦值時,直接使用拷貝構造時最快的方法。這同時也說明了,無名的臨時對象有時候可以被當作中間變量,而不需要從頭進行拷貝構造工作。
C++基本函數的調用優化(構造、拷貝構造、賦值)