要想在編碼過程中柄驻,寫出高效的代碼,是需要自己長期的總結(jié)和不斷學(xué)習(xí)的作谚。工作以來三娩,我自己也總結(jié)了一些小技巧,可以讓你的程序運行的更快妹懒、內(nèi)存空間使用更合理雀监,同時我還會不斷地補充該blog,爭取建立出一個屬于自己的c++ effective系列眨唬。
不多說会前,直接進入正題,以下都是我再編程過程中单绑,總結(jié)出來c++高效編碼規(guī)則回官,每個topic對應(yīng)一個規(guī)則。
局部變量合理使用
讓我們先看一段代碼:
for (int i = 0; i < 1000; ++i)
{
string str = "do some thing:" + int2str(i);
func(str);
}
這段代碼搂橙,在循環(huán)中使用局部變量拼裝函數(shù)func的入?yún)⑶柑幔诿看窝h(huán)過程中,str對象都會執(zhí)行一次構(gòu)造函數(shù)和析構(gòu)函數(shù)区转,那么苔巨,在這個for循環(huán)中,單單是str的組裝就耗費了1000次的內(nèi)存申請和釋放废离,局部變量占用內(nèi)存小的話侄泽,影響不會很大,如果動輒幾十蜻韭、幾百kb悼尾,那就會造成系統(tǒng)內(nèi)存使用的波動,那么是不是有更高效的方法肖方?
其實只需要把str變量放到for循環(huán)外部聲明即可闺魏,如下面代碼:
string str;
for (int i = 0; i < 1000; ++i)
{
str = "do some thing:" + int2str(i);
func(str);
}
這段代碼會大大降低內(nèi)存的申請和釋放次數(shù),因為首次循環(huán)后俯画,str會申請15個字節(jié)的內(nèi)存空間來容納現(xiàn)有數(shù)據(jù)析桥,第二次循環(huán)時,在賦值運算符函數(shù)中艰垂,由于str當(dāng)前空間已經(jīng)足夠容納第二次循環(huán)的數(shù)據(jù)泡仗,因此我們可以考慮對原有str內(nèi)存進行復(fù)用,所以只存在一次數(shù)據(jù)拷貝猜憎,不存在新的內(nèi)存申請和釋放娩怎;到第十次循環(huán)時,需要16個字節(jié)才能容納現(xiàn)有數(shù)據(jù)胰柑,因此需要釋放str原有內(nèi)存峦树,申請新的內(nèi)存辣辫。以此類推,我們可以算出1000次循環(huán)過程中魁巩,只有三次內(nèi)存申請和釋放,大大降低了內(nèi)存的申請和釋放次數(shù)姐浮。
小結(jié):在循環(huán)體中谷遂,局部變量如果占用內(nèi)存空間較大,會造成內(nèi)存使用不合理卖鲤,可以考慮放到循環(huán)體外聲明肾扰。
左值引用的合理使用
左值引用提升程序性能的應(yīng)用場景。
首先是函數(shù)入?yún)⒌坝猓聪旅鎯蓚€函數(shù)的聲明集晚,func1會存在一次str副本的拷貝構(gòu)造的過程,且退出函數(shù)體区匣,還需要釋放str偷拔,而func2直接將str的地址傳入函數(shù)體內(nèi)部,不存在拷貝構(gòu)造亏钩,如果str內(nèi)存很大莲绰,那么節(jié)約一次拷貝的收益還是很可觀的。
void func1(const string str); //存在冗余拷貝構(gòu)造和析構(gòu)
void func2(const string& str); //直接傳遞str變量的地址
其次是循環(huán)體中姑丑,獲取數(shù)組元素時蛤签,如果我們不需要修改原始值,那么應(yīng)該是使用常引用直接指向數(shù)組元素的地址栅哀,避免局部變量的冗余的拷貝構(gòu)造和析構(gòu)
for (int i = 0; i < arrstrs.size(); ++i)
{
string str = arrstrs[i]; //存在冗余拷貝構(gòu)造和析構(gòu)
const string& str = arrstrs[i]; //直接使用arrstrs[i]變量的地址
...
}
動態(tài)數(shù)組容量提前設(shè)定
分層架構(gòu)的代碼中震肮,經(jīng)常出現(xiàn)需要對不同層次數(shù)據(jù)規(guī)格進行轉(zhuǎn)換,即把其他層次的數(shù)據(jù)轉(zhuǎn)化為所在層的數(shù)據(jù)格式留拾,以下是項目中經(jīng)炒辽危看見的一段代碼,主要目的是把第二層的數(shù)據(jù)轉(zhuǎn)化到第一層坐標(biāo)數(shù)據(jù)中间驮,代碼如下:
//變量格式聲明
typedef struct _FirstLayer_PosData_t
{
double x;
double y;
}FirstLayer_PosData_t;
typedef struct _SecondLayer_PosData_t
{
double x;
double y;
int tag;
}SecondLayer_PosData_t;
vector<FirstLayer_PosData_t> arrfir;
vector<SecondLayer_PosData_t> arrsec;
//層數(shù)據(jù)轉(zhuǎn)化代碼
for (int i = 0; i < arrsec.size(); ++i)
{
FirstLayer_PosData_t stFirstLayerPos;
stFirstLayerPos.x = arrsec[i].x;
stFirstLayerPos.y = arrsec[i].y;
arrfir.push_back(stFirstLayerPos);
}
這段代碼可以這樣改進躬厌,其實我們要拷貝的元素個數(shù)是已知的,因此我們可以直接將arrfirst數(shù)組大小設(shè)置為arrsecond的大小即可竞帽,這就避免了在循環(huán)體中動態(tài)的去擴容(每次擴容的成本是先申請新的內(nèi)存空間扛施,將舊內(nèi)存空間數(shù)據(jù)拷貝到新內(nèi)存空間,然后釋放舊內(nèi)存空間)屹篓,改進代碼如下:
arrfir.setsize(arrsec.size());
for (int i = 0; i < arrfir.size(); ++i)
{
FirstLayer_PosData_t stFirstLayerPos;
stFirstLayerPos.x = arrsec[i].x;
stFirstLayerPos.y = arrsec[i].y;
arrfir[i] = stFirstLayerPos;
}
仔細(xì)觀察下疙渣,其實還有優(yōu)化空間,局部變量是可以避免的堆巧,直接使用引用代替第一層數(shù)組的每個元素即可妄荔,最終優(yōu)化代碼如下:
arrfir.setsize(arrsecond.size());
for (int i = 0; i < arrfirst.size(); ++i)
{
FirstLayer_PosData_t& stFirstLayerPos = arrfir[i];
stFirstLayerPos.x = arrsec[i].x;
stFirstLayerPos.y = arrsec[i].y;
}
move語義的合理使用
TODO