宏在 C 語言中非常重要,但在 C++ 中卻無甚大用,普遍的共識:盡量避免使用宏
C++ 之父 Bjarne 在《C++ Programming Language》中寫到
- Avoid macros
《Effective C++》 條款 2
- Prefer const, enum, and inline to #define
谷歌 C++ 編碼規范,關于宏的描述
- Avoid defining macros, especially in headers
- Do not use macros to define pieces of a C++ API
1 禁用宏
谷歌 C++ 規范中,禁用宏的情況有三種:頭檔案、API 介面、程式文本
頭檔案中禁用宏,規范里寫的很明確:
- Don't define macros in a
.hfile.
對于 C++ API 介面,則是:
- Do not use macros to define pieces of a C++ API
因此,如下形式的宏,是禁止的
class PANDA_TYPE(Foo) {
// ...
public:
EXPAND_PUBLIC_PANDA_API(Foo)
EXPAND_PANDA_COMPARISONS(Foo, ==, <)
};
程式文本中禁用宏,尤其是用 ## 來替換變數名
- Don't use macros for program text manipulation
- Prefer not using
##to generate function/class/variable names.
例如,下面代碼是要避免的
#define CAT(a, b) a ## b
#define STRINGIFY(a) #a
void f(int x, int y)
{
string CAT(x, y) = "asdf"; // BAD: hard for tools to handle (and ugly)
string sx2 = STRINGIFY(x);
// ...
}
2 替代宏
《Effective C++》 條款 2:用 const, enum 或 inline 來替代宏
用宏來表示常量和函式,是不推薦的
#define PI 3.14 #define SQUARE(a, b) (a * b)
可用 constexpr 和 模板函式來替代,這樣的好處:constexpr 定義的常量 kPI 會進入符號表,能被編譯器識別到,編譯報錯時會提示 kPI 錯誤
而定義在 .h 中的宏,如果編譯出錯,只會提示 3.14 這個數值的錯誤,對于不是自己寫的頭檔案,且常數含義未知時,很難查到錯誤來源
constexpr double kPI = 3.14; template<typename T>
T square(T a, T b) { return a * b; }
同樣,如下代碼也是需要避免的
// webcolors.h (third party header) #define RED 0xFF0000 #define BLUE 0x0000FF // productinfo.h, the following define product subtypes based on color #define RED 1 #define BLUE 2 int web = BLUE; // web == 2; probably not what was desired
可用 enum class 來代替,在 C++11 之 enum class 中也有提及
enum class Web_color { red = 0xFF0000, green = 0x00FF00, blue = 0x0000FF };
enum class Product_info { red = 0, purple = 1, blue = 2 };
int webby = blue; // error: be specific
Web_color web = Web_color::blue;
3 使用宏
雖然宏在 C++ 中如此被嫌棄,但為了兼容 C 語言,也不能直接將其刪掉,這也是阻礙 C++ 發展的歷史包袱
在某些方面,宏還是有點價值的,比如:頭檔案的保護宏
#ifndef FOO_BAR_BAZ_H_ #define FOO_BAR_BAZ_H_ ... #endif // FOO_BAR_BAZ_H_
還有一些預定義好的宏
__cpluplus __DATE__ __FILE__ __LINE__
在代碼可讀性上,宏往往會有意想不到的效果,如《The Art of Readable Code》中的例子
void AddStats(const Stats& add_from, Stats* add_to)
{
add_to->set_total_memory(add_from.total_memory() + add_to->total_memory());
add_to->set_free_memory(add_from.free_memory() + add_to->free_memory());
add_to->set_swap_memory(add_from.swap_memory() + add_to->swap_memory());
add_to->set_status_string(add_from.status_string() + add_to->status_string());
add_to->set_num_processes(add_from.num_processes() + add_to->num_processes());
...
}
為了增強可讀性,使用宏定義,可改為如下形式
void AddStats(const Stats& add_from, Stats* add_to)
{
#define ADD_FIELD(field) add_to->set_##field(add_from.field() + add_to->field())
ADD_FIELD(total_memory);
ADD_FIELD(free_memory);
ADD_FIELD(swap_memory);
ADD_FIELD(status_string);
ADD_FIELD(num_processes);
...
#undef ADD_FIELD
}
當必須使用宏時,注意如下幾點:
- If you must use macros, use names with capital letters
- Name macros with a project-specific prefix
#definemacros right before you use them, and#undefthem right after.
參考資料
C++ Core GuideLines
谷歌 C++ 編碼規范 - Preprocessor Macros
《The Art of Readable Code》 chapter 8
后記
寫完博文,當我還沉浸在搞清一個 C++ 知識點的興奮中時,突然想到魯迅筆下的《孔乙己》,這篇博文,不就是教茴字四種寫法的現代版么?
孔乙己的悲劇,更多是因時代巨變所致,是舊社會一代讀書人的命運縮影,如果時代沒有變,興許茴字的寫法,也是科舉考試中的一個知識點,
然而,孔乙己還是有一技之長的,"幸而寫得一筆好字,便替人家抄抄書,換一碗飯吃",在如今經濟停滯甚至衰退的浪潮下,我又有什么一技之長 "換一碗飯吃" 呢?
寫到此,我也沒有答案,孔乙己 = 恐怕以為是自己,只能以《孔乙己》的結尾警示自己:我到現在終于沒有見——大約孔乙己的確失業了...
原文鏈接: http://www.cnblogs.com/xinxue/
專注于機器視覺、OpenCV、C++ 編程
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/538609.html
標籤:其他
