1. 宏常量與宏函數
C++中用
#define <宏名> <字元串>
指令定義宏,在代碼中将字元串替換宏名出現的位置。定義宏的方式根據是否包含參數可以分為兩種:
#define <宏名> <字元串>
#define PI 3.1415926
#define <宏名>(<參數清單>) <宏體>
#define A(x) x
複制
2. 使用宏的原因?
在預處理階段的宏替換僅僅是将目标字元串替換宏名,在代碼中對宏的使用必須極其謹慎,否則很容易寫出有問題的程式。定義宏的主要有兩個場景:
- 通過宏定義常量:在常量變更時僅需要修改宏的定義而不需要修改所有使用到常量的位置
- 帶參數的宏可以減少系統調用函數的開銷:對于一些特别簡單的函數而言,函數的調用開銷不可忽視,帶參數的宏在預處理階段就進行了宏展開,提高了程式的運作效率
- 帶參數的宏可以實作模闆功能
3. C++是否應該避免使用宏,如何避免使用宏?
C++原則:盡量使用、
const
和
enum
替換
inline
的使用,防止編譯錯誤不夠明朗,同時加強編譯期間的類型檢查,提高代碼健壯性和可讀性。
#define
3.1 使用const替換#define定義常量
程式編譯分為預處理、編譯和連結三個階段。
#define
是不被視為語言的一部分,在預處理階段就會進行宏展開替換所有的宏,是以進入第二步編譯階段是如果遇到了編譯錯誤,那麼錯誤資訊可能會提到
3.14
而不是
PI
,導緻錯誤資訊不夠明朗。
// 不推薦
#define PI 3.14
// 推薦
const doule Pi = 3.14;
複制
3.2 使用enum替換#define
我們無法使用
#define
建立一個class專屬常量,因為
#define
并不重視作用域。
對于class内定義常量,我們通常使用
static
+
const
的方式定義:
class Student {
private:
static const int num = 10;
int scores[num];
};
const int Student::num; // static 成員變量,需要進行聲明
複制
如果不想外部擷取到
class
專屬常量的記憶體位址,可以使用
enum
的方式定義常量(因為取一個
enum
的位址是不合法的):
class Student {
private:
enum { num = 10 };
int scores[num];
};
複制
3.3 使用inline替換#define
通常使用宏定義函數主要是出于如下考慮:
- 實作模闆功能
- 減少函數調用帶來的開銷
另外一個常見的
#define
誤用情況是以它實作宏函數,它不會招緻函數調用帶來的開銷,但是用
#define
編寫宏函數容易出錯,如下用宏定義寫的求最大值的函數:
#define MAX(a, b) ( { (a) > (b) ? (a) : (b); } ) // 求最大值
複制
這般長相的宏有着太的缺點,比如在下面調用例子:
int a = 6, b = 5;
int max = MAX(a++, b);
std::cout << max << std::endl;
std::cout << a << std::endl;
複制
輸出結果(以下結果是錯誤的):
7 // 正确的答案是 max 輸出 6
8 // 正确的答案是 a 輸出 7
複制
要解釋出錯的原因很簡單,我們把
MAX
宏做簡單替換:
int max = ( { (a++) > (b) ? (a++) : (b); } ); // a 被累加了2次!
複制
在上述替換後,可以發現 a 被累加了 2 次。我們可以通過改進
MAX
宏,來解決這個問題:
#define MAX(a, b) ({ \
__typeof(a) __a = (a), __b = (b); \
__a > __b ? __a : __b; \
})
複制
簡單說明下,上述的
__typeof
可以根據變量的類型來定義一個相同類型的變量。改進後的
MAX
宏,輸出的是正确的結果,
max
輸出 6,
a
輸出 7。
雖然改進的後
MAX
宏,解決了問題,但是這種宏的長相就讓人困惑。我們可以用
template inline
的方式,寫出短小的函數:
template<typename T>
inline T max(const T& a, const T& b)
{
return a > b? a : b;
}
複制