1. 程式人生 > 其它 >Effective C++ 條款2:儘量以const,enum,inline替換#define

Effective C++ 條款2:儘量以const,enum,inline替換#define

#define用來定義常量。

首先我們需要知道,#define會在預編譯的時候,以字串替換的形式被替換掉。假設我們#define AspectRatio= 1.635。如果AspectRatio在使用的過程中報錯,編譯器會直接提示1.635。如果這個巨集是其他檔案定義的,那麼對於使用者而言,他甚至都不知道1.635是個什麼東西。

下面還有兩種特殊情況

定義常量指標

常量指標通常需要用兩個const。即既限定指標指向物件無法改變,也限定無法通過指標改變物件值。因為如果用#define定義指標的話,通常定義的是一個常量地址。這個地址代表的一定是一個固定的東西,因此必須是指標常量。並且,我們當然也不希望通過地址值直接修改任何東西。所以也必須是常量指標。

下面是個例子。

const char* const author_name = "Scott Meyers";

這裡面定義的一個指標,就是指標常量。但通常我們用string而非char*

const std::string author_name("Scott Meyers");

class專屬常量

類中的常量,我們希望他只有一版,因此需要定義為靜態的。

class GamePlayer{
private:
    static const int number_turns = 5;
    int scores[number_turns];
}

但由於上面的這個常量是寫在類中的,因此只是一個宣告,而非定義。在取地址的時候,如果這個常量是內建的整數型,int,char,bool,就沒什麼問題。但是如果是別的類,或者有的其他編譯器不允許這樣,那就要在外面定義一下。

const int GamePlayer::number_turns = 5;

因為C++要求任何東西都要被定義才能使用。

enum hack

上述問題還有一個解決方法,就是enum hack。列舉型的變數可以當成一個int使用。而且列舉型可能比const更像#define。因為const變數可以取地址,但是enum和#define都無法取地址。

class GamePlayer {
private:
    enum {num_turns = 5};
    int scores[num_turn];
}

濫用巨集

這一塊我們討論一個小問題,就是把巨集定義成非常複雜的函式。這樣可能導致一些意外的結果。下面是個例子

#define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b))
int a = 5, b = 0;
CALL_WITH_MAX(++a, b)

由於巨集會直接替換,所以最後就成了

f((++a) > (b) ? (++a) : (b))

很顯然,我們只希望a自加一次,但是這裡卻加了兩次。

解決這個問題的方法是使用模板行內函數

template<typename T>
inline void callWithMax(const T& a, const T& b) {
    f(a > b ? a : b)
}

#define 設計的通常都是些簡單的函式,因此完全可以將他們設計成行內函數。最大的好處是,模板行內函數是個函式,他可以放在class當中。這樣就可以為它設計訪問性,比如public和private。但是巨集定義是沒有類中許可權的。

結論

  • 對於單純常量,用const替換
  • 對於形似函式的巨集,最好用inline替換