C++程序設(shè)計(jì)實(shí)踐

字號(hào):

隨著計(jì)算機(jī)語(yǔ)言的發(fā)展,我們現(xiàn)在編寫一個(gè)程序越來越容易了。利用一些軟件開發(fā)工具,往往只要通過鼠標(biāo)的拖拖點(diǎn)點(diǎn),計(jì)算機(jī)就會(huì)自動(dòng)幫你生成許多代碼。但在很多時(shí)候,計(jì)算機(jī)的這種能力被濫用了,我們往往只考慮把這個(gè)程序搭起來,而不去考慮程序的性能如何,程序是否足夠的健壯。而此節(jié)課的目的主要是介紹一些編碼的經(jīng)驗(yàn),讓大家編寫的程序更加健壯和高性能。
    1、Prefer const and inline to #define
    在C++編程中應(yīng)該盡量使用const和inline來代替#define,盡量做到能不用#define就不用。#define常見的用途有“定義常量”以及“定義宏”,但其中存在諸多的弊病。
    第一,查錯(cuò)不直觀,不利于調(diào)試。Define的定義是由預(yù)處理程序處理的,作的是完全的文本替換,不做任何的類型檢查。在編譯器處理階段,define定義的東西已經(jīng)被完全替換了,這樣在debug的時(shí)候就看不到任何的相關(guān)信息,即跟蹤時(shí)不能step into宏。例如,把ASPECT_RATIO用define定義成1.653,編譯器就看不到ASPECT_RATIO這個(gè)名字了。如果編譯器報(bào)1.653錯(cuò),那么就無從知道此1.653來自于何處。在真正編碼的時(shí)候應(yīng)該使用如下的語(yǔ)句來定義:
    static const double ASPECT_RATIO = 1.653;
    第二,沒有任何類型信息,不是type safe。因?yàn)樗俏谋炯?jí)別的替換,這樣不利于程序的維護(hù)。
    第三,define的使用很容易造成污染。比如,如果有兩個(gè)頭文件都定義了ASPECT_RATIO, 而一個(gè)CPP文件又同時(shí)包含了這兩個(gè)頭文件,那么就會(huì)造成沖突。更難查的是另外一種錯(cuò)誤,比如有如下的代碼:
    // 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又成為了一種顏色,那么編譯器就會(huì)把此處的Orange替換成2,編譯可能仍然可以通過,程序也能夠運(yùn)行,但是這就成了一個(gè)bug,表現(xiàn)出古怪的錯(cuò)誤,且很難查錯(cuò)。再比如定義了一個(gè)求a與b哪個(gè)數(shù)大的宏,#define max(a,b) ((a) > (b) ? (a) : (b))
    int a = 5, b = 0;
    max(++ a, b);
    max(++ a, b + 10);
    在上面的操作中,max(++ a, b); 語(yǔ)句中a被++了兩次,而max(++ a, b + 10); 語(yǔ)句中a只加了,這樣在程序處理中就很有可能成為一個(gè)bug,且此bug也非常的難找。在實(shí)際編碼時(shí)可以使用如下的語(yǔ)句來做:
    template
    inline const T&
    max(const T& a, const T& b) { return a > b ? a : b; }
    2、Prefer C++-style casts
    在程序中經(jīng)常會(huì)需要把一種類型轉(zhuǎn)換成另外一種類型,在C++中應(yīng)該使用static_cast、const_cast、dynamic_cast、reinterpret_cast關(guān)鍵字來做類型轉(zhuǎn)換。因?yàn)檫@有以下好處,一是其本身就是一種注釋,在代碼中看到上面這些關(guān)鍵字就可馬上知道此處是進(jìn)行類型轉(zhuǎn)換。二是C語(yǔ)言中類型轉(zhuǎn)換通常是很難進(jìn)行搜索的,而通過關(guān)鍵字cast則可以很容易的找到程序中出現(xiàn)類型轉(zhuǎn)換的地方了。
    3、Distinguish between prefix and postfix forms of increment and decrement operators
    通常對(duì)于操作系統(tǒng)或編譯器自身支持的類型,prefix(前綴,如++i)與postfix(后綴,如i++)的效果是一樣的。因?yàn)楝F(xiàn)在的編譯器都很聰明,它會(huì)自動(dòng)做優(yōu)化,這兩者的匯編代碼是一樣的,性能不會(huì)有差別。但有時(shí)候也會(huì)有不同的,如一些重載了操作符的類型。下面是模擬prefix與postfix的操作過程,可以發(fā)現(xiàn)在postfix操作中會(huì)生成一個(gè)臨時(shí)變量,而這一臨時(shí)變量是會(huì)占用額外的時(shí)間和開銷的。
    // 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ū)分是先++,還是后++,但是我們?cè)诰帉懗绦虻臅r(shí)候好能習(xí)慣性的將其寫成++i的形式,如在使用STL中的iterator時(shí),prefix與postfix會(huì)有相當(dāng)大的性能差異。請(qǐng)不要小看這些細(xì)節(jié),實(shí)際在編寫程序的時(shí)候,若不注意具體細(xì)節(jié),你會(huì)發(fā)現(xiàn)程序的性能會(huì)非常的低。但要注意,雖然在大多數(shù)情況下可以用prefix來代替postfix,但有一種情況例外,那就是有[]操作符時(shí),比如gzArray [++index] 是不等于 gzArray[index++]的。