• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            子彈 の VISIONS

            NEVER back down ~~

            C++博客 首頁 新隨筆 聯(lián)系 聚合 管理
              112 Posts :: 34 Stories :: 99 Comments :: 0 Trackbacks
            [轉(zhuǎn)]C++程序設計最佳實踐
            2007年04月23日 星期一 00:57

              隨著計算機語言的發(fā)展,我們現(xiàn)在編寫一個程序越來越容易了。利用一些軟件開發(fā)工具,往往只要通過鼠標的拖拖點點,計算機就會自動幫你生成許多代碼。但在很多時候,計算機的這種能力被濫用了,我們往往只考慮把這個程序搭起來,而不去考慮程序的性能如何,程序是否足夠的健壯。而此節(jié)課的目的主要是介紹一些編碼的經(jīng)驗,讓大家編寫的程序更加健壯和高性能。

              1、Prefer const and inline to #define

              在C++編程中應該盡量使用const和inline來代替#define,盡量做到能不用#define就不用。#define常見的用途有“定義常量”以及“定義宏”,但其中存在諸多的弊病。

              第一,查錯不直觀,不利于調(diào)試。Define的定義是由預處理程序處理的,作的是完全的文本替換,不做任何的類型檢查。在編譯器處理階段,define定義的東西已經(jīng)被完全替換了,這樣在debug的時候就看不到任何的相關(guān)信息,即跟蹤時不能step into宏。例如,把ASPECT_RATIO用define定義成1.653,編譯器就看不到ASPECT_RATIO這個名字了。如果編譯器報1.653錯,那么就無從知道此1.653來自于何處。在真正編碼的時候應該使用如下的語句來定義:


            static const double ASPECT_RATIO = 1.653;

              第二,沒有任何類型信息,不是type safe。因為它是文本級別的替換,這樣不利于程序的維護。

              第三,define的使用很容易造成污染。比如,如果有兩個頭文件都定義了ASPECT_RATIO, 而一個CPP文件又同時包含了這兩個頭文件,那么就會造成沖突。更難查的是另外一種錯誤,比如有如下的代碼:
              // in header file def.h
              #define Apple 1
              #define Orange 2
                #define Pineapple 3
               …
              // in some cpp file that includes the def.h
              enum Colors {White, Black, Purple, Orange};

              在.h文件中Orange被定義成水果的一種,而在.cpp文件中Orange又成為了一種顏色,那么編譯器就會把此處的Orange替換成2,編譯可能仍然可以通過,程序也能夠運行,但是這就成了一個bug,表現(xiàn)出古怪的錯誤,且很難查錯。再比如定義了一個求a與b哪個數(shù)大的宏,#define max(a,b) ((a) > (b) ? (a) : (b))
              int a = 5, b = 0;
              max(++ a, b);
              max(++ a, b + 10);

              在上面的操作中,max(++ a, b); 語句中a被++了兩次,而max(++ a, b + 10); 語句中a只加了一次,這樣在程序處理中就很有可能成為一個bug,且此bug也非常的難找。在實際編碼時可以使用如下的語句來做:
              template
              inline const T&
              max(const T& a, const T& b) { return a > b ? a : b; }

              2、Prefer C++-style casts

              在程序中經(jīng)常會需要把一種類型轉(zhuǎn)換成另外一種類型,在C++中應該使用static_cast、const_cast、dynamic_cast、reinterpret_cast關(guān)鍵字來做類型轉(zhuǎn)換。因為這有以下好處,一是其本身就是一種注釋,在代碼中看到上面這些關(guān)鍵字就可馬上知道此處是進行類型轉(zhuǎn)換。二是C語言中類型轉(zhuǎn)換通常是很難進行搜索的,而通過關(guān)鍵字cast則可以很容易的找到程序中出現(xiàn)類型轉(zhuǎn)換的地方了。

              3、Distinguish between prefix and postfix forms of increment and decrement operators

              通常對于操作系統(tǒng)或編譯器自身支持的類型,prefix(前綴,如++i)與postfix(后綴,如i++)的效果是一樣的。因為現(xiàn)在的編譯器都很聰明,它會自動做優(yōu)化,這兩者的匯編代碼是一樣的,性能不會有差別。但有時候也會有不同的,如一些重載了操作符的類型。下面是模擬prefix與postfix的操作過程,可以發(fā)現(xiàn)在postfix操作中會生成一個臨時變量,而這一臨時變量是會占用額外的時間和開銷的。
              // prefix form: increment and fetch
              UPInt& UPInt::operator++()
               {
                *this += 1; // increment
               return *this; // fetch
               }
              // postfix form: fetch and increment
               const UPInt UPInt::operator++(int)
               {
                UPInt oldValue = *this; // fetch
               ++(*this); // increment
                return oldValue; // return what was fetched
               }

              一般情況下不需要區(qū)分是先++,還是后++,但是我們在編寫程序的時候最好能習慣性的將其寫成++i的形式,如在使用STL中的iterator時,prefix與postfix會有相當大的性能差異。請不要小看這些細節(jié),實際在編寫程序的時候,若不注意具體細節(jié),你會發(fā)現(xiàn)程序的性能會非常的低。但要注意,雖然在大多數(shù)情況下可以用prefix來代替postfix,但有一種情況例外,那就是有[]操作符時,比如gzArray [++index] 是不等于 gzArray[index++]的。

            4、Minimizing Compile-time Dependencies

              有些人在編寫程序時,往往喜歡將一個.h文件包含到另一個.h文件,而實踐證明在做大型軟件時這是一個非常不好的習慣,因這樣會造成很多依賴的問題,包含較多的.h文件,別人又使用了這個class,而在他的那個工程中可能并不存在這些.h文件,這樣很可能就編譯不能通過。而且這樣做,還可能造成很難去更新一個模塊的情況。因為一個.h文件被很多模塊包含的話,如果修改了此.h文件,在編譯系統(tǒng)的時候,編譯器會去尋找哪些模塊依賴于某個被修改過的.h文件,那么就導致了所有包含入此.h文件的模塊全都要進行重新編譯。在項目比較小的時候,大家可能還感覺不到差別,但是如果說是在大型的軟件系統(tǒng)里,你可能編譯一遍源碼需要七、八個小時。如果你這個.h文件被很多模塊包含的話,就算在.h文件中加了一行注釋,在編譯時編譯器檢查哪些文件被改動,那么所有包含入此.h文件的模塊都會被重新編譯,造成巨大的時間和精力負擔。對于此問題,解決的方法就是讓.h文件自包含,也就是說讓它包含盡量少的東西。所謂盡量少是指如刪掉任何一個它包含進來的.h文件,都將無法正常進行工作。其實在很多情況下,并不需要一個.h文件去包含另一個.h文件,完全可以通過class聲明來解決依賴關(guān)系的這種問題。再來看下面這個例子:
              #include "a.h" // class A
              #include "b.h" // class B
              #include "c.h" // class C
              #include "d.h" // class D
              #include "e.h" // class E
              class X : public A, private B
              {
               public:
              E SomeFunctionCall(E someParameter);
               private:
               D m_dInstance;
              };

              當類X從類A和類B中派生時,需要知道X在內(nèi)存中都有哪些data,通常在內(nèi)存中前面是基類的data,后面緊跟的是此派生類自身定義的data,因此就必須知道類A與類B的內(nèi)部細節(jié),要不然編譯器就無法來安排內(nèi)存了。但是在處理參數(shù)以及參數(shù)返回值的時候,實際上并不需要知道這些信息,在此處定義的SomeFunctionCall()只需知道E是個class就足夠了,并不需要知道類E中的data如長度等的具體細節(jié)。上面的代碼應該改寫成如下的形式,以減少依賴關(guān)系:
              #include "a.h" // class A
              #include "b.h" // class B
              #include "c.h" // class C
              #include "d.h" // class D
              class E;
              class X : public A, private B
              {
               public:
              E SomeFunctionCall(E someParameter);
               private:
              D m_dInstance;
              };

              5、Never treat arrays polymorphically

              不要把數(shù)組和多態(tài)一起使用,請看下面的例子。
              class BST { ... };
              class BalancedBST: public BST { ... };
              void printBSTArray(ostream& s, const BST array[], int numElements)
              {
              for (int i = 0; i < numElements; ++i)
              {
               s << array[i];
              // this assumes an operator<< is defined for BST
              }
              }

              BalancedBST bBSTArray[10];
              printBSTArray(cout, bBSTArray, 10);

              數(shù)組在內(nèi)存中是一個連續(xù)的內(nèi)存空間,而在數(shù)組中應該如何來定位一個元素呢?過程是這樣的,編譯器可以知道每個數(shù)據(jù)類型的長度大小,如果數(shù)組的index是0,則會自動去取第一個元素;如果是指定了某個index,編譯器則會根據(jù)此index與該數(shù)據(jù)類型的長度自動去算出該元素的位置。

              在printBSTArray()函數(shù)中,盡管傳入的參數(shù)是BalancedBST類型,但由于其本來定義的類型是BST,那么它依然會根據(jù)BST來計算類型的長度。而通常派生類實例所占的內(nèi)存要比基類實例所占的內(nèi)存大一些,因此該程序在編譯時會報錯。請記住,永遠不要把數(shù)組和C++的多態(tài)性放在一起使用。

              6、Prevent exceptions from leaving destructors

              析構(gòu)函數(shù)中一定不要拋出異常。通常有兩種情況會導致析構(gòu)函數(shù)的調(diào)用,一種是當該類的對象離開了它的域,或delete表達式中一個該類對象的指針,另一種是由于異常而引起析構(gòu)函數(shù)的調(diào)用。

              如果析構(gòu)函數(shù)被調(diào)用是由于exception引起,而此時在析構(gòu)函數(shù)中又拋出了異常,程序會立即被系統(tǒng)終止,甚至都來不及進行內(nèi)存釋放。因此如果在析構(gòu)函數(shù)中拋出異常的話,就很容易混淆引起異常的原因,且這樣的軟件也會讓用戶非常惱火。由于析構(gòu)函數(shù)中很可能會調(diào)用其它的一些函數(shù),所以在寫析構(gòu)函數(shù)的時候一定要注意,對這些函數(shù)是否會拋出異常要非常清楚,如果會的話,就一定要小心了。比如下面這段代碼:
              Session::~Session()
              {
              logDestruction(this);
              }

              比如logDestruction()函數(shù)可能會拋出異常,那么我們就應該采用下面這種代碼的形式:
              Session::~Session()
              {
               try
              {
               logDestruction(this);
               }
               catch (...)
              {
               }
             }

              這樣程序出錯的時候不會被立即關(guān)掉,可以給用戶一些其它的選擇,至少先讓他把目前在做的工作保存下來。

              7、Optimization:Remember the 80-20 rule

              在軟件界有一個20-80法則,其實這是一個很有趣的現(xiàn)象,比如一個程序中20%的代碼使用了該程序所占資源的80%;一個程序中20%的代碼占用了總運行時間的80%;一個程序中20%的代碼使用了該程序所占內(nèi)存的80%;在20%的代碼上面需要花費80%的維護力量,等等。這個規(guī)律還可以被繼續(xù)推廣下去,不過這個規(guī)律無法被證明,它是人們在實踐中觀察得出的結(jié)果。從這個規(guī)律出發(fā),我們在做程序優(yōu)化的時候,就有了針對性。比如想提高代碼的運行速度,根據(jù)這個規(guī)律可以知道其中20%的代碼占用了80%的運行時間,因此我們只要找到這20%的代碼,并進行相應的優(yōu)化,那么我們程序的運行速度就可以有較大的提高。再如有一個函數(shù),占用了程序80%的運行時間,如果把這個函數(shù)的執(zhí)行速度提高10倍,那么對程序整體性能的提高,影響是非常巨大的。如果有一個函數(shù)運行時間只占總時間的1%,那就算把這個函數(shù)的運行速度提高1000倍,對程序整體性能的提高也是影響不大的。所以我們的基本思想就是找到占用運行時間最大的那個函數(shù),然后去優(yōu)化它,哪怕只是改進了一點點,程序的整體性能也可以被提高很多。

              要想找出那20%的代碼,我們的方法就是使用Profiler,它實際上是一些公司所開發(fā)的工具,可以檢查程序中各個模塊所分配內(nèi)存的使用情況,以及每個函數(shù)所運行的時間等。常見的Profiler有Intel公司開發(fā)的VTune,微軟公司開發(fā)的Visual Studio profiler,DevPartner from Compuware等。

            posted on 2008-07-16 17:12 子彈のVISIONS 閱讀(332) 評論(0)  編輯 收藏 引用 所屬分類: 1.x 臨時目錄
            精品久久久久久亚洲精品| 色综合久久久久网| 国产精品成人无码久久久久久 | 久久久久久午夜成人影院 | 97精品国产97久久久久久免费| 波多野结衣久久精品| 中文字幕久久久久人妻| 国产亚洲精久久久久久无码| 色综合久久中文色婷婷| 一本一本久久aa综合精品| 久久久精品午夜免费不卡| 久久久99精品成人片中文字幕 | 久久精品国产72国产精福利| 国产A级毛片久久久精品毛片| 久久青草国产精品一区| av色综合久久天堂av色综合在| 久久久久亚洲av无码专区| 青青热久久国产久精品 | 亚州日韩精品专区久久久| 国内精品久久久久久野外| 亚洲精品乱码久久久久久按摩 | 亚洲中文久久精品无码ww16| 精品久久久久久综合日本| 久久婷婷人人澡人人| 精品无码久久久久国产| 婷婷久久综合| 久久涩综合| 欧美久久一级内射wwwwww.| 久久久久久久尹人综合网亚洲| 狠狠色综合网站久久久久久久高清| 99久久精品无码一区二区毛片| 久久99精品久久久久久hb无码| 久久笫一福利免费导航 | 好久久免费视频高清| 久久久亚洲欧洲日产国码二区| 国产精品亚洲综合久久| 区亚洲欧美一级久久精品亚洲精品成人网久久久久 | 午夜福利91久久福利| 久久综合久久性久99毛片| 久久综合九色综合久99| 亚洲&#228;v永久无码精品天堂久久|