欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

提高C++程序運(yùn)行效率的10個簡單方法

 更新時間:2014年09月04日 17:10:38   投稿:shichen2014  
這篇文章主要介紹了提高C++程序運(yùn)行效率的10個簡單方法,包括了循環(huán)、變量、繼承等等應(yīng)用的技巧,非常具有實(shí)用價值,需要的朋友可以參考下

本文以C/C++程序?yàn)槔v述了程序運(yùn)行效率的10個簡單方法,分享給大家供大家參考之用。具體分析如下:

對于每一個程序員來說,程序的運(yùn)行效率都是一個值得重視,并為之付出努力的問題。但是程序性能的優(yōu)化也是一門復(fù)雜的學(xué)問,需要很多的知識,然而并不是每個程序員都具備這樣的知識,而且論述如何優(yōu)化程序提高程序運(yùn)行效率的書籍也很少。但是這并不等于我們可以忽略程序的運(yùn)行效率,下面就介紹一下本人積累的一些簡單實(shí)用的提高程序運(yùn)行效率的方法,希望對大家有所幫助。

一、盡量減少值傳遞,多用引用來傳遞參數(shù)。
至于其中的原因,相信大家也很清楚,如果參數(shù)是int等語言自定義的類型可能能性能的影響還不是很大,但是如果參數(shù)是一個類的對象,那么其效率問題就不言而喻了。例如一個判斷兩個字符串是否相等的函數(shù),其聲明如下:

bool Compare(string s1, string s2)
bool Compare(string *s1, string *s2)
bool Compare(string &s1, string &s2)
bool Compare(const string &s1, const string &s2)

其中若使用第一個函數(shù)(值傳遞),則在參數(shù)傳遞和函數(shù)返回時,需要調(diào)用string的構(gòu)造函數(shù)和析構(gòu)函數(shù)兩次(即共多調(diào)用了四個函數(shù)),而其他的三個函數(shù)(指針傳遞和引用傳遞)則不需要調(diào)用這四個函數(shù)。因?yàn)橹羔樅鸵枚疾粫?chuàng)建新的對象。如果一個構(gòu)造一個對象和析構(gòu)一個對象的開銷是龐大的,這就是會效率造成一定的影響。

然而在很多人的眼中,指針是一個惡夢,使用指針就意味著錯誤,那么就使用引用吧!它與使用普通值傳遞一樣方便直觀,同時具有指針傳遞的高效和能力。因?yàn)橐檬且粋€變量的別名,對其操作等同于對實(shí)際對象操作,所以當(dāng)你確定在你的函數(shù)是不會或不需要變量參數(shù)的值時,就大膽地在聲明的前面加上一個const吧,就如最后的一個函數(shù)聲明一樣。

同時加上一個const還有一個好處,就是可以對常量進(jìn)行引用,若不加上const修飾符,引用是不能引用常量的。

二、++i和i++引申出的效率問題

看了上面的第一點(diǎn),你可能覺得,那不就是多調(diào)用了四個函數(shù)而已,你可能對此不屑一顧。那么來看看下面的例子,應(yīng)該會讓你大吃一驚。

至于整型變量的前加和后加的區(qū)別相信大家也是很清楚的。然而在這里我想跟大家談的卻是C++類的運(yùn)算符重載,為了與整形變量的用法一致,在C++中重載運(yùn)算符++時一般都會把前加和后加都重載。你可能會說,你在代碼中不會重載++運(yùn)算符,但是你敢說你沒有使用過類的++運(yùn)算符重載嗎?迭代器類你總使用過吧!可能到現(xiàn)在你還不是很懂我在說什么,那么就先看看下面的例子吧,是本人為鏈表寫的一個內(nèi)部迭代器。

_SingleList::Iterator& _SingleList::Iterator::operator++()//前加
{
  pNote = pNote->pNext;
  return *this;
}
_SingleList::Iterator _SingleList::Iterator::operator++(int)//后加
{
  Iterator tmp(*this);
  pNote = pNote->pNext;
  return tmp;
}

從后加的實(shí)現(xiàn)方式可以知道,對象利用自己創(chuàng)建一個臨時對象(自己在函數(shù)調(diào)用的一個復(fù)制),然后改變自己的狀態(tài),并返回這個臨時對象,而前加的實(shí)現(xiàn)方式時,直接改變自己的內(nèi)部狀態(tài),并返回自己的引用。

從第一點(diǎn)的論述可以知道后加實(shí)現(xiàn)時會調(diào)用復(fù)制構(gòu)造函數(shù),在函數(shù)返回時還要調(diào)用析構(gòu)函數(shù),而由于前加實(shí)現(xiàn)方式直接改變對象的內(nèi)部狀態(tài),并返回自己的引用,至始至終也沒有創(chuàng)建新的對象,所以也就不會調(diào)用構(gòu)造函數(shù)和析構(gòu)函數(shù)。

然而更加糟糕的是,迭代器通常是用來遍歷容器的,它大多應(yīng)用在循環(huán)中,試想你的鏈表有100個元素,用下面的兩種方式遍歷:

for(_SingleList::Iterator it = list.begin(); it != list.end(); ++it)
{
  //do something
} 

for(_SingleList::Iterator it = list.begin(); it != list.end(); it++)
{
  //do something
} 

如果你的習(xí)慣不好,寫了第二種形式,那么很不幸,做同樣的事情,就是因?yàn)橐粋€前加和一個后加的區(qū)別,你就要調(diào)用多200個函數(shù),其對效率的影響可就不可忽視了。

三、循環(huán)引發(fā)的討論1(循環(huán)內(nèi)定義,還是循環(huán)外定義對象)

請看下面的兩段代碼:

代碼1:

ClassTest CT;
for(int i = 0; i < 100; ++i)
{
  CT = a;
  //do something
}

代碼2:

for(int i = 0; i < 100; ++i)
{
  ClassTest CT = a;
  //do something
}

你會覺得哪段代碼的運(yùn)行效率較高呢?代碼1科學(xué)家是代碼2?其實(shí)這種情況下,哪段代碼的效率更高是不確定的,或者說是由這個類ClassTest本向決定的,分析如下:

對于代碼1:需要調(diào)用ClassTest的構(gòu)造函數(shù)1次,賦值操作函數(shù)(operator=)100次;對于代碼2:需要高用(復(fù)制)構(gòu)造函數(shù)100次,析構(gòu)函數(shù)100次。

如果調(diào)用賦值操作函數(shù)的開銷比調(diào)用構(gòu)造函數(shù)和析構(gòu)函數(shù)的總開銷小,則第一種效率高,否則第二種的效率高。

四、循環(huán)引發(fā)的討論2(避免過大的循環(huán))

現(xiàn)在請看下面的兩段代碼,
代碼1:

for(int i = 0; i < n; ++i)
{
  fun1();
  fun2();
}

代碼2:

for(int i = 0; i < n; ++i)
{
  fun1();
}
for(int i = 0; i < n; ++i)
{
  fun2();
}

注:這里的fun1()和fun2()是沒有關(guān)聯(lián)的,即兩段代碼所產(chǎn)生的結(jié)果是一樣的。

以代碼的層面上來看,似乎是代碼1的效率更高,因?yàn)楫吘勾a1少了n次的自加運(yùn)算和判斷,畢竟自加運(yùn)算和判斷也是需要時間的。但是現(xiàn)實(shí)真的是這樣嗎?

這就要看fun1和fun2這兩個函數(shù)的規(guī)模(或復(fù)雜性)了,如果這多個函數(shù)的代碼語句很少,則代碼1的運(yùn)行效率高一些,但是若fun1和fun2的語句有很多,規(guī)模較大,則代碼2的運(yùn)行效率會比代碼1顯著高得多??赡苣悴幻靼走@是為什么,要說是為什么這要由計(jì)算機(jī)的硬件說起。

由于CPU只能從內(nèi)存在讀取數(shù)據(jù),而CPU的運(yùn)算速度遠(yuǎn)遠(yuǎn)大于內(nèi)存,所以為了提高程序的運(yùn)行速度有效地利用CPU的能力,在內(nèi)存與CPU之間有一個叫Cache的存儲器,它的速度接近CPU。而Cache中的數(shù)據(jù)是從內(nèi)存中加載而來的,這個過程需要訪問內(nèi)存,速度較慢。

這里先說說Cache的設(shè)計(jì)原理,就是時間局部性和空間局部性。時間局部性是指如果一個存儲單元被訪問,則可能該單元會很快被再次訪問,這是因?yàn)槌绦虼嬖谥h(huán)??臻g局部性是指如果一個儲存單元被訪問,則該單元鄰近的單元也可能很快被訪問,這是因?yàn)槌绦蛑写蟛糠种噶钍琼樞虼鎯?、順序?zhí)行的,數(shù)據(jù)也一般也是以向量、數(shù)組、樹、表等形式簇聚在一起的。

看到這里你可能已經(jīng)明白其中的原因了。沒錯,就是這樣!如果fun1和fun2的代碼量很大,例如都大于Cache的容量,則在代碼1中,就不能充分利用Cache了(由時間局部性和空間局部性可知),因?yàn)槊垦h(huán)一次,都要把Cache中的內(nèi)容踢出,重新從內(nèi)存中加載另一個函數(shù)的代碼指令和數(shù)據(jù),而代碼2則更很好地利用了Cache,利用兩個循環(huán)語句,每個循環(huán)所用到的數(shù)據(jù)幾乎都已加載到Cache中,每次循環(huán)都可從Cache中讀寫數(shù)據(jù),訪問內(nèi)存較少,速度較快,理論上來說只需要完全踢出fun1的數(shù)據(jù)1次即可。

五、局部變量VS靜態(tài)變量

很多人認(rèn)為局部變量在使用到時才會在內(nèi)存中分配儲存單元,而靜態(tài)變量在程序的一開始便存在于內(nèi)存中,所以使用靜態(tài)變量的效率應(yīng)該比局部變量高,其實(shí)這是一個誤區(qū),使用局部變量的效率比使用靜態(tài)變量要高。

這是因?yàn)榫植孔兞渴谴嬖谟诙褩V械?,對其空間的分配僅僅是修改一次esp寄存器的內(nèi)容即可(即使定義一組局部變量也是修改一次)。而局部變量存在于堆棧中最大的好處是,函數(shù)能重復(fù)使用內(nèi)存,當(dāng)一個函數(shù)調(diào)用完畢時,退出程序堆棧,內(nèi)存空間被回收,當(dāng)新的函數(shù)被調(diào)用時,局部變量又可以重新使用相同的地址。當(dāng)一塊數(shù)據(jù)被反復(fù)讀寫,其數(shù)據(jù)會留在CPU的一級緩存(Cache)中,訪問速度非??臁6o態(tài)變量卻不存在于堆棧中。

可以說靜態(tài)變量是低效的。

六、避免使用多重繼承

在C++中,支持多繼承,即一個子類可以有多個父類。書上都會跟我們說,多重繼承的復(fù)雜性和使用的困難,并告誡我們不要輕易使用多重繼承。其實(shí)多重繼承并不僅僅使程序和代碼變得更加復(fù)雜,還會影響程序的運(yùn)行效率。

這是因?yàn)樵贑++中每個對象都有一個this指針指向?qū)ο蟊旧?,而C++中類對成員變量的使用是通過this的地址加偏移量來計(jì)算的,而在多重繼承的情況下,這個計(jì)算會變量更加復(fù)雜,從而降低程序的運(yùn)行效率。而為了解決二義性,而使用虛基類的多重繼承對效率的影響更為嚴(yán)重,因?yàn)槠淅^承關(guān)系更加復(fù)雜和成員變量所屬的父類關(guān)系更加復(fù)雜。

七、盡量少使用dynamic_cast

dynamic_cast的作用是進(jìn)行指針或引用的類型轉(zhuǎn)換,dynamic_cast的轉(zhuǎn)換需要目標(biāo)類型和源對象有一定的關(guān)系:繼承關(guān)系。 實(shí)現(xiàn)從子類到基類的指針轉(zhuǎn)換,實(shí)際上這種轉(zhuǎn)換是非常低效的,對程序的性能影響也比較大,不可大量使用,而且繼承關(guān)系越復(fù)雜,層次越深,其轉(zhuǎn)換時間開銷越大。在程序中應(yīng)該盡量減少使用。

八、減少除法運(yùn)算的使用

無論是整數(shù)還是浮點(diǎn)數(shù)運(yùn)算,除法都是一件運(yùn)算速度很慢的指令,在計(jì)算機(jī)中實(shí)現(xiàn)除法是比較復(fù)雜的。所以要減少除法運(yùn)算的次數(shù),下面介紹一些簡單方法來提高效率:
1、通過數(shù)學(xué)的方法,把除法變?yōu)槌朔ㄟ\(yùn)算,如if(a > b/c),如果a、b、c都是正數(shù),則可寫成if(a*c > b)
2、讓編譯器有優(yōu)化的余地,如里你要做的運(yùn)算是int型的n/8的話,寫成(unsigned)n/8有利于編譯器的優(yōu)化。而要讓編譯器有優(yōu)化的余地,則除數(shù)必須為常數(shù),而這也可以用const修飾一個變量來達(dá)到目的。

九、將小粒度函數(shù)聲明為內(nèi)聯(lián)函數(shù)(inline)

正如我們所知,調(diào)用函數(shù)是需要保護(hù)現(xiàn)場,為局部變量分配內(nèi)存,函數(shù)結(jié)束后還要恢復(fù)現(xiàn)場等開銷,而內(nèi)聯(lián)函數(shù)則是把它的代碼直接寫到調(diào)用函數(shù)處,所以不需要這些開銷,但會使程序的源代碼長度變大。

所以若是小粒度的函數(shù),如下面的Max函數(shù),由于不需要調(diào)用普通函數(shù)的開銷,所以可以提高程序的效率。

int Max(int a, int b)
{
  return a>b?a:b;
}

十、多用直接初始化

與直接初始化對應(yīng)的是復(fù)制初始化,什么是直接初始化?什么又是復(fù)制初始化?舉個簡單的例子,

ClassTest ct1;
ClassTest ct2(ct1);  //直接初始化
ClassTest ct3 = ct1;  //復(fù)制初始化

那么直接初始化與復(fù)制初始化又有什么不同呢?直接初始化是直接以一個對象來構(gòu)造另一個對象,如用ct1來構(gòu)造ct2,復(fù)制初始化是先構(gòu)造一個對象,再把另一個對象值復(fù)制給這個對象,如先構(gòu)造一個對象ct3,再把ct1中的成員變量的值復(fù)制給ct3,從這里,可以看出直接初始化的效率更高一點(diǎn),而且使用直接初始化還是一個好處,就是對于不能進(jìn)行復(fù)制操作的對象,如流對象,是不能使用賦值初始化的,只能進(jìn)行直接初始化。可能我說得不太清楚,那么下面就引用一下經(jīng)典吧!

以下是Primer是的原話:

當(dāng)用于類類型對象時,初始化的復(fù)制形式和直接形式有所不同:直接初始化直接調(diào)用與實(shí)參匹配的構(gòu)造函數(shù),復(fù)制初始化總是調(diào)用復(fù)制構(gòu)造函數(shù)。復(fù)制初始化首先使用指定構(gòu)造函數(shù)創(chuàng)建一個臨時對象,然后用復(fù)制構(gòu)造函數(shù)將那個臨時對象復(fù)制到正在創(chuàng)建的對象”,還有一段這樣說,“通常直接初始化和復(fù)制初始化僅在低級別優(yōu)化上存在差異,然而,對于不支持復(fù)制的類型,或者使用非explicit構(gòu)造函數(shù)的時候,它們有本質(zhì)區(qū)別:
ifstream file1("filename")://ok:direct initialization
ifstream file2 = "filename";//error:copy constructor is private

注:如還對直接初始化和復(fù)制初始化有疑問,可以參考一下前面的一篇文章:
C++直接初始化與復(fù)制初始化的區(qū)別深入解析,里面有有關(guān)直接初始化和復(fù)制初始化的詳細(xì)解釋。

補(bǔ)充:

這里只是一點(diǎn)點(diǎn)的建議,雖然說了這么多,但是還是要說一下的就是:要避免不必要的優(yōu)化,避免不成熟的優(yōu)化,不成熟的優(yōu)化的是錯誤的來源,因?yàn)榫幾g器會為你做很多你所不知道的優(yōu)化

希望本文所述對提高大家C++程序設(shè)計(jì)效率能有所幫助。

相關(guān)文章

  • c++函數(shù)指針和回調(diào)函數(shù)示例

    c++函數(shù)指針和回調(diào)函數(shù)示例

    這篇文章主要介紹了c++函數(shù)指針和回調(diào)函數(shù)示例,需要的朋友可以參考下
    2014-05-05
  • C語言SetConsoleCursorInfo函數(shù)使用方法

    C語言SetConsoleCursorInfo函數(shù)使用方法

    這篇文章介紹了C語言SetConsoleCursorInfo函數(shù)的使用方法,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-12-12
  • C++輸入流和輸出流 超級詳細(xì)

    C++輸入流和輸出流 超級詳細(xì)

    C++ 的開發(fā)者認(rèn)為數(shù)據(jù)輸入和輸出的過程也是數(shù)據(jù)傳輸?shù)倪^程,數(shù)據(jù)像水一樣從一個地方流動到另一個地方,所以 C++ 中將此過程稱為“流”,實(shí)現(xiàn)此過程的類稱為“流類”。下面小編將詳細(xì)介紹這個話題,需要的朋友可以參考一下
    2021-09-09
  • vscode刷acm、leetcode的題目

    vscode刷acm、leetcode的題目

    vscode是一款越來越受碼農(nóng)們喜愛的軟件,大多數(shù)人學(xué)習(xí)編程繞不開的一部分就是算法,很多人都喜歡刷LeetCode的題目,本文就來介紹一下
    2021-06-06
  • C++程序中使用Windows系統(tǒng)Native Wifi API的基本教程

    C++程序中使用Windows系統(tǒng)Native Wifi API的基本教程

    這篇文章主要介紹了C++程序中使用Windows系統(tǒng)Native Wifi API的基本教程,包括在程序中控制無線網(wǎng)卡開關(guān)的方法,需要的朋友可以參考下
    2016-03-03
  • C++中內(nèi)存分區(qū)及其作用分析

    C++中內(nèi)存分區(qū)及其作用分析

    C++內(nèi)存分區(qū)包括棧區(qū)、堆區(qū)、全局靜態(tài)區(qū)、常量區(qū),各自負(fù)責(zé)不同的數(shù)據(jù)存儲和回收,棧區(qū)主要用于存放函數(shù)局部變量和參數(shù),堆區(qū)用于動態(tài)分配內(nèi)存,全局靜態(tài)區(qū)用于存放全局靜態(tài)變量和靜態(tài)成員變量,常量區(qū)用于存放常量和字符串常量
    2023-04-04
  • C++結(jié)構(gòu)體與類的區(qū)別詳情

    C++結(jié)構(gòu)體與類的區(qū)別詳情

    這篇文章主要介紹了C++結(jié)構(gòu)體與類的區(qū)別,C++中的struct對C中的struct進(jìn)行了擴(kuò)充,它已經(jīng)不再只是一個包含不同數(shù)據(jù)類型的數(shù)據(jù)結(jié)構(gòu)了,它已經(jīng)獲取了太多的功能。下面我們一起進(jìn)入文章倆姐具體內(nèi)容,需要的朋友也可以參考一下
    2021-11-11
  • C++實(shí)操之內(nèi)聯(lián)成員函數(shù)介紹

    C++實(shí)操之內(nèi)聯(lián)成員函數(shù)介紹

    大家好,本篇文章主要講的是C++實(shí)操之內(nèi)聯(lián)成員函數(shù)介紹,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽
    2021-12-12
  • C++實(shí)現(xiàn)車票管理系統(tǒng)

    C++實(shí)現(xiàn)車票管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)車票管理系統(tǒng),連接數(shù)據(jù)庫MySQL,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • OpenCV圖像分割中的分水嶺算法原理與應(yīng)用詳解

    OpenCV圖像分割中的分水嶺算法原理與應(yīng)用詳解

    這篇文章主要為大家詳細(xì)介紹了OpenCV圖像分割中的分水嶺算法原理與應(yīng)用,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-01-01

最新評論