宏定义的使用建议
1)虽然宏定义很灵活,并且通过彼此结合可以产生许多变形用法,但是 C++/C 程序员不要定义很复杂的宏,宏定义应该简单而清晰。
2)宏名采用大写字符组成的单词或其缩写序列,并在各单词之间使用“_”分隔。
3)如果需要公布某个宏,那么该宏定义应当放置在头文件中,否则放置在实现文件(.cpp)的顶部。
4)不要使用宏来定义新类型名,应该使用 typedef,否则容易造成错误。
5)给宏添加注释时请使用块注释(/* */),而不要使用行注释。因为有些编译器可能会把宏后面的行注释理解为宏体的一部分。
6)尽量使用 const 取代宏来定义符号常量。
7)对于较长的使用频率较高的重复代码片段,建议使用函数或模板而不要使用带参数的宏定义;而对于较短的重复代码片段,可以使用带参数的宏定义,这不仅是出于类型安全的考虑,而且也是优化与折衷的体现。
8)尽量避免在局部范围内(如函数内、类型定义内等)定义宏,除非它只在该局部范围内使用,否则会损害程序的清晰性。
1.宏定义#与##的区别和使用
- 定义
#define f(a,b) a##b // (1)
#define g(a) #a // (2)
#define h(a) g(a) // (3)
- 使用情况
printf("%s\n", g(f(1, 2)));
printf("%s\n", h(f(1, 2)));
输出以下结果:
f(1, 2)
12
- 解释
1)符号#表示“字符串化”的意思,就是把跟在后面的参数转换成一个字符串,
例如上述代码中g(f(1, 2))输出的结果为f(1, 2)
2)符号##是一个连接符号,用于把参数连在一起,例如a##b等同于ab
- 注意点
1)如果宏定义是带#的,如上所示,则直接替换:g(f(1,2))等于#f ( 1 , 2 ),也就是“ f (1 , 2) ”
2)如果宏定义是不带#的,如h(f(1, 2 ) ) 所示,基本原则为展开参数然后替换。也就是h(12),也就是 "12"
- 总体步骤
步骤为:由外层向里层走,如果碰到的是以非#开头的宏,则继续往里层走,直到最里层,然后开始往外层展开。如果碰到的是以#开头的宏,则不再往里层走,往外层展开。h(f(1,2))—>h(12)—>g(12)—>#12—>“12”
- 代码举例
char a = 'c';
cout << g(a) << endl; // "a"
cout << g(g(a)) << endl; // "g(a)"
printf("%s\n", h(f(1, 2))); // "12"
printf("%s\n", g(f(1, 2))); // "f(1,2)"
printf("%s\n", g(h(f(1, 2)))); // "h(f(1,2))"
printf("%s\n", h(g(f(1, 2)))); // ""f(1,2)""
printf("%s\n", h(h(f(1, 2)))); // ""12""
2.算数组的大小
//定义
#ifndef ARR_LEN
#define ARR_LEN(a) (sizeof(a) / sizeof(*(a)))
#endif
#ifndef DIM
#define DIM(arrar_name) (sizeof(arrar_name) / sizeof(arrar_name[0]))
#endif
//使用
int targets_len = ARR_LEN(_targets); //这个_targets是数组的大小
3.宏定义比较大小
//取小
template<class T=int>
const T& tmin(const T&a , const T&b)
{
return (a>b)?b:a;
}
//取大
template<class T = int>
const T& tmax(const T&a , const T&b)
{
return (a<b)?b:a;
}
4.算术符优先问题
#define COUNT(M) M*M
int x=5;
print(COUNT(x+1));
print(COUNT(++X));
//结果输出:11 和42 而不是函数的输出36
- 注意:
1)预编译器只是进行简单的文本替换,COUNT(x+1)被替换成 COUNT(x+1x+1),5+15+1=11,而不是 36
2)CUNT(++x)被替换成++x*++x 即为 67=42,而不是想要的 66=36,连续前置自加加两次
5、容器遍历
// vector 的循环简化处理
#define FOREACH_VECTOR(_array, _condition, _do) for (size_t i=0 ; i < _array.size(); i++){ if (_condition){_do;} }
// 例子: FOREACH_VECTOR(m_vtMembers, m_vtMembers[i]->m_hActorID == hActorID, return m_vtMembers[i]);
#define FOREACH_IN_CONTAINTER(itNAME, CONTAINER) for(auto itNAME = (CONTAINER).begin(); itNAME != (CONTAINER).end(); ++itNAME )
#define FOREACH_IN_CONTAINTER_C(itNAME, CONTAINER) for(auto itNAME = (CONTAINER).cbegin(); itNAME != (CONTAINER).cend(); ++itNAME )
#define FOREACH_IN_CONTAINTER_R(itNAME, CONTAINER) for(auto itNAME = (CONTAINER).rbegin(); itNAME != (CONTAINER).rend(); ++itNAME )
6、判断空字符串
// 判断是否为空字符串
#ifndef IS_STR_EMPTY
#define IS_STR_EMPTY(x) ((x)[0]=='\0')
#endif
7、复制字符串
// 复制字符串
#ifndef MY_STRNCPY
#define MY_STRNCPY(dst, src, maxlen) strncpy((dst), (src), (maxlen)-1);(dst)[(maxlen)-1]='\0';
#endif
8、导出dll的函数
#if (defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64))
#ifndef DLL_EXPORT
#define DLL_EXPORT __declspec(dllexport)
#endif
- 使用例子
DLL_EXPORT void myFunction() {
// function implementation
}
This will tell the compiler to export the myFunction function from the DLL. Other modules that link to the DLL will then be able to access this function.