`
yidongkaifa
  • 浏览: 4064616 次
文章分类
社区版块
存档分类
最新评论

C++中的宏

 
阅读更多
Technorati 标签: C++,,#define

#define <macro_name> [MACRO_VALUE] <br>#undef <macro_name></macro_name></macro_name>

#define可以用于定义常量、条件编译、宏函数等用法,无论如何使用宏,必须记住其本质,宏只是简单的文本替换,只是将MACRO_NAME直接替换为MACRO_VALUE包含的文本,而#undef用于取消定义。

在工程上,为了便于区分宏与变量,宏一般用大写字母表示,单词之间使用下划线“_”分隔,以便于阅读。

1.1.1. 定义常量

#define PI 3.14

#define ERROR -1

#define UINT16_MAX 65535

因为宏的本质是简单的文本替换,所以本身不包含类型信息,即不保证类型安全,所以很多书上都建议使用常量代替宏[??]。如,上面的UINT16_MAX使用常量表示为:

const unsigned short UNINT16_MAX = 65535;

如果定义一组相关的常量,就可以考虑使用枚举或枚举类(参见??)。

使用宏定义常量,而不是硬编码便于代码维护,例如我们曾经使用一个整数-99表示数据未出始化,后来我们重构代码,想换为-1,只需要改一个宏就可以了,如[??],如果是硬编码,则需要将上百个-99改为-1,虽然现在的多数编辑器都具有搜索替换功能,但谁也不能保证把所有-99替换为-1就可以了,很可能出错。

#define INT_NOT_SUPPORT -1 // from -99

定义常量的宏用法常用于定义一些代码的配置项,一般定义一个config.h文件,然后在该文件中放置一些配置,而在其它代码文件中引用该头件,可以随时根据需要修改配置,重新编译。例如,在实现一个线程池时,可以通过配置文件配置编程池的大小(当然也可以动态配置)。

// config.h

#define THREAD_POOL_SIZE 10

#define THREAD_DEFAULT_STACK_SIZE (2*1024) // 2KB

1.1.2. 条件编译

#define常与#ifdef/#ifndef/defined指令配合使用,用于条件编译,例如我们曾经有个项目使用了boost::asio网络库,其它几个平台都很好,而在HP-UX 11.23上使用HP aC++却不能编译通过,后来使用了Socket++代替了boost::asio,通过条件编译可以方便地选择使用哪个库。

// config.h

#define USING_BOOST_ASIO // 也可以在Makfile或Visual Studio的工程文件中定义

// xxx.cpp

#include “config.h”

int SendTo(const string& ip, const int port, const string& msg)

{

#if defined(USING_BOOST_ASIO

// code using boost::asio

#elif defined(USING_SOCKET_PLUS_PLUS)

// code using socket++

#endif

}

在条件编译时建议使用#if defined和#if !defined来代替使用#ifdef/#ifndef,因为前者更方便处理多分支的情况与较复杂条件表达式的情况,如一段使用#if defined的代码,通过定义操作系统的相关宏OS_HPUX、OS_AIX或操作系统版本HPUX_11_11、HPUX_11_23、HPUX_11_31编译不同的程序代码,以封装底层实现代码,使程序达到可移植的效果。

#if defined(OS_HPUX)&&(defined(HPUX_11_11)|| defined(HPUX_11_23)

// for HP-UX 11.11 and 11.23

#elif defined(OS_HPUX) && defined(HPUX_11_31

// for HP-UX 11.31

#elif defined(OS_AIX)

// for AIX

#else

#endif

如果使用#ifdef将使代码得很难读,如[??],如果再加入新的条件,嵌套的层数越多,代码越难维护,所以在条件编译时尽可能使用#if defined而不是#ifdef。

#ifdef OS_HPUX

# ifdef HPUX_11_11

// for HPUX 11.11

# endif

# ifdef HPUX_11_23

// for HPUX 11.23

# endif

# ifdef HPUX_11_31

// for HPUX 11.31

# endif

#else

# ifdef OS_AIX

// for AIX

# else

# endif

#endif

条件编译时,如果一个文件中太多条件编译的代码,有些编辑器的智能感知可能都不能很好地解析,还是保持代码越简单越好。对于函数级别的条件编译主要有两种实现方式:

(1) 同一个函数声明,同一个函数定义,函数体内使用条件编译代码。这种方式有个问题,如果条件编译代码太多,会导致这个函数体很长,不利于阅读与维护;有一个优点是,有利于编辑器的智能感知,因为这样解析函数名比较方便,但随着编辑器功能的完善,这方面的差别就不明显了。例如[??]的SendTo函数。

(2) 根据编译条件,将编译条件相同的代码放到单独的文件中,这些文件在顶层文件中使用条件编译指令来引用。如[??],在Google的浏览器Chrome源代码中普遍采用了这种方式。这种方式最大的优点就是不同平台的程序由不同的源文件来实现,很便于多人分工合作,对于某一部分代码由一个人实现并测试完成后直接把源文件复制过来就可以了,进行低层次的单元测试非常方便;它的缺点就是增加了目录中的文件数量。

// func.cpp

#if defined(OS_WINDOWS)

#include “func_win.cpp”

#elif defined(OS_LINUX)

#include “func_linux.cpp”

#elif defined(OS_SOLARIS)

#include “func_solaris.cpp”

#else

#error Unknown OS

#endif

// func_win.cpp

void func( void ) { /* implementation for Windows */ }

// func_solaris.cpp

void func( void ) { /* implementation for Solaris */ }

// func_linux.cpp

void func( void ) { /* implementation for Linux */ }

1.1.3. 宏函数

宏参数其实不是函数,而是带参数的宏,通过参数使某些代码可以重用,使代码便于维护,或达到特定目的。

1.1.3.1. 避免函数调用,提高程序效率

常用的就是最大值与最小值的判断函数,由于函数内容并不多,如果定义为函数在调用比较频繁的场合会明显降低程序的效率,其实宏是用空间效率换取了时间效率。如取两个值的最大值:

#define MAX(a,b) ((a)

定义为函数:

inline int Max(int a, int b) { return a

定义为模板:

template <typename t></typename>

inline T TMax(T a, T b) { return a

使用宏函数的优点有两个:(1)适用于任何实现了operator

需要注意的是,由于宏的本质是直接的文本替换,所以在宏函数的“函数体”内都要把参数使用括号括起来,防止参数是表达式时造成语法错误或结果错误,如[??]。

#define MIN( a, b) b

#define SUM( a, b) a + b

cout

int c = SUM(a,b)*2; // c的期望值:16,实际值:13

1.1.3.2. 引用编译期数据

上述的这些作用虽然使用宏函数可以取得更好的性能,但如果从功能上讲完全可以不使用宏函数,而使用模板函数或普通函数实现,但还有些时候只能通过宏实现。例如,程序中在执行某些操作时可能会失败,此时要打印出失败的代码位置,只能使用宏实现。

#define SHOW_CODE_LOCATION() cout

if( 0 != rename(“oldFileName”, “newFileName”) ){

cout

SHOW_CODE_LOCATION();

}

虽然宏是简单的替换,所以在调用宏函数SHOW_CODE_LOCATION时,分号可以直接写到定义里,也可以写到调用处,但最好还是写到调用处,看起来更像是调用了函数,否则看着代码不伦不类,如[??]。

#define SHOW_CODE_LOCATION() cout

if( 0 != rename(“oldFileName”, “newFileName”) ){

cout

SHOW_CODE_LOCATION()

}

1.1.3.3. do-while的秒用

do-while循环控制语句的特点就是循环体内的语句至少会被执行一次,如果while(…)内的条件始终为0时,循环体内的语句就会被执行且只被执行一次,这样的执行效果与直接使用循环体内的代码相同,但这们会得到更多的益处。

#define SWAP_INT(a, b) do{ /

int tmp = a; /

a = b; /

b = tmp; /

}while(0)

int main( void )

{

int x = 3, y = 4;

if( x > y )

SWAP_INT(x, y);

return 0;

}

代码[??],通过do-while代码块的宏定义我们不仅可以把SWAP_INT像函数一样用,而且还有优点:

l 在宏定义中可以使用局部变量;

l 在宏定义中可以包含多个语句,但可以当作一条语句使用,如代码中的if分支语句,如果没有do-while把多条语句组织成一个代码块,则程序的运行结果就不正确,甚至不能编译。

其实我们定义的SWAP_INT(a, b)相当于定义了引用参数或指针参数的函数,因为它可以改变实参的值。在C++0X中有了decltype关键词,这种优势就更显示了,因为在宏中使用了局部变量必须确定变量的类型,所以这个宏只能用于交换int型的变量值,如果换作其它类型则还必须定义新的宏,如SWAP_FLOAT、SWAP_CHAR等,而通过decltype,我们就可以定义一个万能的宏。

#include <iostream></iostream>

using namespace std;

#define SWAP(a, b) do{ /

decltype(a) tmp = a; /

a = b; /

b = tmp; /

}while(0)

int main( void )

{

int a = 1, b = 2;

float f1 = 1.1f, f2 = 2.2f;

SWAP(a, b);

SWAP(f1,f2);

return 0;

}

通过宏实现的SWAP“函数”要比使用指针参数效率还要高,因为它连指针参数都不用传递而是使用直接代码,对于一些效率要求比较明显的场合,宏还是首选。

1.1.4. 取消宏定义(#undef)

#undef指令用于取消前面用#define定义的宏,取消后就可以重新定义宏。该指令用的并不多,因为过多的#undef会使代码维护起来非常困难,一般也只用于配置文件中,用来清除一些#define的开关,保证宏定义的唯一性,如[??]。

// config.h

#undef HAS_OPEN_SSL

#undef HAS_ZLIB

#if defined(HAS_OPEN_SSL)

#endif

#if defined(HAS_ZLIB)

#endif

将对该头文件的引用放到所有代码文件的第一行,就可以保证HAS_OPEN_SSL没有被定义,即使是在编译选项里定义过一宏,也会被#undef指令取消,这样使得config.h就是唯一一处放置条件编译开关的地方,更有利于维护。

分享到:
评论

相关推荐

    Visual C++源代码 192 如何使用自动化运行Excel宏

    Visual C++源代码 192 如何使用自动化运行Excel宏Visual C++源代码 192 如何使用自动化运行Excel宏Visual C++源代码 192 如何使用自动化运行Excel宏Visual C++源代码 192 如何使用自动化运行Excel宏Visual C++源代码...

    c++中的宏、内联函数和宏的比较

    c++中的宏、内联函数和宏的比较 c++中的宏、内联函数和宏的比较 c++中的宏、内联函数和宏的比较 c++中的宏、内联函数和宏的比较 c++中的宏、内联函数和宏的比较

    C++中各种颜色宏定义

    平常在开发的时候会用到各种颜色RGB值定义,很多时候需要到绘图里查看各颜色的RGB值。该头文件把各种颜色进行了宏定义,使用时只要包含该头文件即可。 不仅能够加快开发速度,还能使颜色值更加明了,方便阅读代码。

    C++宏定义说明(详解)

    C++_宏定义说明(详解)

    c++宏编程技巧代码

    一些c++宏技巧; 用于批量产生代码; 学习用; 优先推荐boost的preprocessor; 原帖: http://blog.csdn.net/CDScan/archive/2009/11/24/4863057.aspx

    c++宏的使用总结.pdf

    c++宏的使用总结,非常实用经典,详细讲解了宏的使用和特性

    C++ 常用宏定义

    有关宏定义的总结,从什么地方copy过来的忘记了。

    c++语言 工具 宏替换工具

    可以将自己定义的模版代码展开(利用宏的定义),便于在不使用c++模版时使用。随程序附带了2个实例

    C++/C 宏定义(define)中# ## 的含义

    介绍C++/C 宏定义(define)中# ## 的含义, 并以C++代码举例说明

    c++进阶自定义宏的介绍

    首先,__cplusplus是cpp中的自定义宏,那么定义了这个宏的话表示这是一段cpp的代码,也就是说,上面的代码的含义是:如果这是一段cpp的代码,那么加入extern "C"{和}处理其中的代码。  要明白为何使用extern "C",...

    c/c++宏定义 宏定义的入门教材 基础

    c/c++ 宏定义 基础,让你从基础起,去了解宏的定义,让你的程序更精彩。

    Visual C++源代码 188 如何使用自动化运行Word宏

    Visual C++源代码 188 如何使用自动化运行Word宏Visual C++源代码 188 如何使用自动化运行Word宏Visual C++源代码 188 如何使用自动化运行Word宏Visual C++源代码 188 如何使用自动化运行Word宏Visual C++源代码 188...

    C++宏定义的计算器

    新手入门专用,其实只要看我写的代码,就能快速掌握C++了,本人自己写的,代码简单,通俗易懂,老幼皆宜,除此之外更多源码全都由本人原创完成,适合新手,程序员开发着研究。

    C/C++ 宏详解(详解)

    介绍了C/C++ 宏的用法和常用错误,众多C++书籍都忠告我们C语言宏是万恶之首,但事情总不如我们想象的那么坏,就如同goto一样。宏有 一个很大的作用,就是自动为我们产生代码。如果说模板可以为我们产生各种型别的...

    Visual C++ MFC 中常用宏的含义

    本片文档概括简介了Visual C++ MFC 中常见的宏定义,比较简单,共同分享。

    c与c++头文件兼容宏定义

    很多时候我们为c与c++函数的头文件不能兼容而不知所措,此文档详细介绍如何实现他们之间的兼容

    C、C++中变参数宏

    C C++中常见预处理宏定义描述 可变参数宏 和 VA ARGS

    ARM 编译器为 C 和 C++ 预定义的宏

    ARM 编译器为 C 和 C++ 预定义的宏,从别的网站下载的!

    VC RGB宏执行过程分析

    分析Visual C++中RGB宏的转换过程。

Global site tag (gtag.js) - Google Analytics