C++性能優(yōu)化之一:合理使用內存

要想在編碼過程中,寫出高效的代碼,是需要自己長期的總結和不斷學習的。工作以來,我自己也總結了一些小技巧,可以讓你的程序運行的更快、內存空間使用更合理,同時我還會不斷地補充該blog,爭取建立出一個屬于自己的c++ effective系列。

不多說,直接進入正題,以下都是我再編程過程中,總結出來c++高效編碼規(guī)則,每個topic對應一個規(guī)則。

局部變量合理使用

讓我們先看一段代碼:

for (int i = 0; i < 1000; ++i)
{
    string str = "do some thing:" + int2str(i);
    func(str);
}

這段代碼,在循環(huán)中使用局部變量拼裝函數(shù)func的入參,在每次循環(huán)過程中,str對象都會執(zhí)行一次構造函數(shù)和析構函數(shù),那么,在這個for循環(huán)中,單單是str的組裝就耗費了1000次的內存申請和釋放,局部變量占用內存小的話,影響不會很大,如果動輒幾十、幾百kb,那就會造成系統(tǒng)內存使用的波動,那么是不是有更高效的方法?

其實只需要把str變量放到for循環(huán)外部聲明即可,如下面代碼:

string str;
for (int i = 0; i < 1000; ++i)
{
    str = "do some thing:" + int2str(i);
    func(str);
}

這段代碼會大大降低內存的申請和釋放次數(shù),因為首次循環(huán)后,str會申請15個字節(jié)的內存空間來容納現(xiàn)有數(shù)據,第二次循環(huán)時,在賦值運算符函數(shù)中,由于str當前空間已經足夠容納第二次循環(huán)的數(shù)據,因此我們可以考慮對原有str內存進行復用,所以只存在一次數(shù)據拷貝,不存在新的內存申請和釋放;到第十次循環(huán)時,需要16個字節(jié)才能容納現(xiàn)有數(shù)據,因此需要釋放str原有內存,申請新的內存。以此類推,我們可以算出1000次循環(huán)過程中,只有三次內存申請和釋放,大大降低了內存的申請和釋放次數(shù)。

小結:在循環(huán)體中,局部變量如果占用內存空間較大,會造成內存使用不合理,可以考慮放到循環(huán)體外聲明。

左值引用的合理使用

左值引用提升程序性能的應用場景。
首先是函數(shù)入參,看下面兩個函數(shù)的聲明,func1會存在一次str副本的拷貝構造的過程,且退出函數(shù)體,還需要釋放str,而func2直接將str的地址傳入函數(shù)體內部,不存在拷貝構造,如果str內存很大,那么節(jié)約一次拷貝的收益還是很可觀的。

void func1(const string str);  //存在冗余拷貝構造和析構
void func2(const string& str); //直接傳遞str變量的地址

其次是循環(huán)體中,獲取數(shù)組元素時,如果我們不需要修改原始值,那么應該是使用常引用直接指向數(shù)組元素的地址,避免局部變量的冗余的拷貝構造和析構

for (int i = 0; i < arrstrs.size(); ++i)
{
    string str = arrstrs[i];        //存在冗余拷貝構造和析構
    const string& str = arrstrs[i]; //直接使用arrstrs[i]變量的地址
    ...
}

動態(tài)數(shù)組容量提前設定

分層架構的代碼中,經常出現(xiàn)需要對不同層次數(shù)據規(guī)格進行轉換,即把其他層次的數(shù)據轉化為所在層的數(shù)據格式,以下是項目中經??匆姷囊欢未a,主要目的是把第二層的數(shù)據轉化到第一層坐標數(shù)據中,代碼如下:

//變量格式聲明
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ù)據轉化代碼
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ù)組大小設置為arrsecond的大小即可,這就避免了在循環(huán)體中動態(tài)的去擴容(每次擴容的成本是先申請新的內存空間,將舊內存空間數(shù)據拷貝到新內存空間,然后釋放舊內存空間),改進代碼如下:

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;
}

仔細觀察下,其實還有優(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

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,658評論 1 32
  • 1.設計模式是什么? 你知道哪些設計模式,并簡要敘述?設計模式是一種編碼經驗,就是用比較成熟的邏輯去處理某一種類型...
    龍飝閱讀 2,302評論 0 12
  • 最全的iOS面試題及答案 iOS面試小貼士 ———————————————回答好下面的足夠了-----------...
    zweic閱讀 2,803評論 0 73
  • 每個人都是因為偶然和幸運而生,或是星星,或是花草,或是泥石,或是風雨,各自都有自己所屬的星空。如果你是一朵花,閃亮...
    總督島秦觀閱讀 228評論 2 1
  • 六年前的今天,差不多也是這個時間,我們奔赴在人生第一次與彼此見面的路上; 兩年前的今天,我們領證了; 一年前的今天...
    茶顏閱讀 259評論 0 0

友情鏈接更多精彩內容