小白学习大型C++源码项目系列之宏定义

文章目录

    • enum
    • enum代码实战
    • 宏定义
    • 预先编译命令条件编译
    • 内联函数
    • 先说宏和函数的区别

c++枚举、预编译宏定义、内联函数、用法原理详细解读

enum

为什么要用枚举?

比如说定义一个这样的enum Days {Sat=1, Sun, Mon, Tue, Wed, Thu, Fri};
如果在程序中想表示星期1时可以直接写成Days.Mon比较直观而不是用数字来表示,
方便以后维护; 而且。扩充性也比较强,容易维护。限定了取值范围,也不容易出错 
直观,提高代码可读性直接写数字会让人莫名其妙的~ 

常类型的变量或对象的值是不能被更新的

常变量: const 类型说明符 变量名
常引用: const 类型说明符 &引用名
常数组: 类型说明符 const 数组名[大小]
常指针: const 类型说明符* 指针名 ,类型说明符* const 指针名
常对象: 类名 const 对象名
常数据成员: const 类型说明符 数据成员名
常成员函数: 类名::fun(形参) const

C/C++语言可以使用#define和const创建符号常量,而使用enum工具不仅能够创建符号常量,还能定义新的数据类型,

enum enumType {Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday};
这句话有两个作用:
第一:声明enumType为新的数据类型,称为枚举(enumeration);
第二:声明Monday、Tuesday等为符号常量,通常称之为枚举量,其值默认分别为0-6。(后面会介绍怎样显式的初始化枚举量的值)
枚举元素是常量,不能对它们赋值,不能写赋值表达式:Sunday = 0
枚举元素具有默认值,它们依次为: 0,1,2,......。
对于枚举,只定义了赋值运算符,没有为枚举定义算术运算。
枚举类型取值方法是(枚举类型的变量)枚举元素的默认值例如:(color_set)j
switch 语句与 if 语句不同的是,switch 语句只能够测试是否相等,
因此,case 语句后面只能是整型或字符型的常量或常量表达式;
而在 if 语句中还能够测试关系与逻辑表达式。
在 switch 语句中,每个 case 语句的结尾不要忘记添加 break 语句
在 switch 语句中,default 语句主要用于检查默认情况,或者处理错误情况

enum代码实战

#include 
#include
using namespace std;
int main(){
    enum color_set {red,yellow,blue,white,black}; //声明枚举类型color
    color_set color;//定义一个枚举类型的变量color
    int i,j,k,counter=0,loop; //counter是累计不同颜色的组合数
    for(i=red;i<=black;i++)
    {
        for(j=red;j<=black;j++)
        {
            if(i!=j)
            {                        //前两个球颜色不同
                for(k=red;k<=black;k++)
                    if(k!=i&&k!=j)
                    {        //第三个球不同于前两个,满足要求
                        counter++;
                        if((counter)%22==0)
                        { //每屏显示22行
                            cout<<"请按回车键继续";
                            cin.get();
                        }
                        cout<

预处理功能包括宏定义、文件包含、条件编译等

宏定义

所谓宏定义,就是用一个标识符来表示一个字符串,如果在后面的代码中出现了该标识符,那么就全部替换成指定的字符串。

预先编译命令条件编译

条件编译是指预处理器根据条件编译指令,有条件地选择源程序代码中的一部分代码作为输出,送给编译器进行编译。主要是为了有选择性地执行相应操作,防止宏替换内容(如文件等)的重复包含。

在编译和链接之前,还需要对源文件进行一些文本方面的操作,比如文本替换、文件包含、删除部分代码等,这个过程叫做预处理,由预处理程序完成。

源文件要经过编译、链接才能生成可执行程序

   (1)编译(Compile)会将源文件(.c文件)转换为目标文件。对于 VC/VS,目标文件后缀为.obj;对于GCC,目标文件后缀为.o。(编译是针对单个源文件的,一次编译操作只能编译一个源文件,如果程序中有多个源文件,就需要多次编译操作。)
   (2)链接(Link)是针对多个文件的,它会将编译生成的多个目标文件以及系统中的库、组件等合并成一个可执行程序。
写法 格式 解释
#include 头文件包含命令,尖括号:编译器会到系统路径下查找头文件;双引号:编译器会先在当前目录下查找头文件,如果没有找到,再到系统路径下查找
#define 宏定义,无参宏定义的一般形式为:#define 宏名 字符串;带参宏定义的一般形式为:#define 宏名(参数表) 字符串;
#undef 上文提到#define的作用域是从它声明开始到文件结尾,#undef就是取消之前的宏定义(也就是#define的标识符)
#ifdef 小白学习大型C++源码项目系列之宏定义_第1张图片 它的意思是,如果该宏已被定义过,则对程序段1进行编译,否则对程序段2进行编译(这个和上面的#if一样最后都需要#endif),上述格式也可以不用#else,这一点上和if else相同
#endif 如果该宏已经定义,则执行相应操作
#ifndef 小白学习大型C++源码项目系列之宏定义_第2张图片 如果该宏未被定义,则对“程序段1”进行编译,否则对“程序段2”进行编译,一般用于检测程序中是否已经定义了名字为某标识符的宏,如果没有定义该宏,则定义该宏,并选中从 #define 开始到 #endif 之间的程序段;如果已定义,则不再重复定义该符号,且相应程序段不被选中。
#if 小白学习大型C++源码项目系列之宏定义_第3张图片 执行起来就是,如果整形常量表达式为真,则执行程序段1,否则继续往后判断依次类推(注意是整形常量表达式),最后#endif是#if的结束标志
#elif #elif相当于if else语句中的else if()语句,需要注意的是该语句是#elif,而不是#elseif
#else 如果前面条件均为假,则执行相应操作
#endif #endif是#if, #ifdef, #ifndef这些条件命令的结束标志
#error 指令用于在编译期间产生错误信息,并阻止程序的编译

内联函数

内联函数的执行过程与带参数宏定义很相似,但参数的处理不同。带参数的宏定义并不对参数进行运算,而是直接替换;内联函数首先是函数,这就意味着函数的很多性质都适用于内联函数,即内联函数先把参数表达式进行运算求值,然后把表达式的值传递给形式参数。

内联函数与带参数宏定义的另一个区别是,内联函数的参数类型和返回值类型在声明中都有明确的指定;而带参数宏定义的参数没有类型的概念,只有在宏展开以后,才由编译器检查语法,这就存在很多的安全隐患。

使用内联函数时,应注意以下问题:

1)内联函数的定义性声明应该出现在对该函数的第一次调用之前。

2)内联函数首先是函数,函数的很多性质都适用于内联函数,如内联函数可以重载。

3)在内联函数中不允许使用循环语句和switch结果,带有异常接口声明的函数也不能声明为内联函数。

先说宏和函数的区别

  1. 宏做的是简单的字符串替换(注意是字符串的替换,不是其他类型参数的替换),而函数的参数的传递,参数是有数据类型的,可以是各种各样的类型。

  2. 宏的参数替换是不经计算而直接处理的,而函数调用是将实参的值传递给形参,既然说是值,自然是计算得来的。

  3. 宏在编译之前进行,即先用宏体替换宏名,然后再编译的,而函数显然是编译之后,在执行时,才调用的。因此,宏占用的是编译的时间,而函数占用的是执行时的时间。

  4. 宏的参数是不占内存空间的,因为只是做字符串的替换,而函数调用时的参数传递则是具体变量之间的信息传递,形参作为函数的局部变量,显然是占用内存的。

  5. 函数的调用是需要付出一定的时空开销的,因为系统在调用函数时,要保留现场,然后转入被调用函数去执行,调用完,再返回主调函数,此时再恢复现场,这些操作,显然在宏中是没有的。

现在来看内联函数

所谓“内联函数”就是将很简单的函数“内嵌”到调用他的程序代码中,只样做的目的是为了避免上面说到的第5点,目的旨在节约下原本函数调用时的时空开销。但必须注意的是:作为内联函数,函数体必须十分简单,不能含有循环、条件、选择等复杂的结构,否则就不能做为内联函数了。事实上,即便你没有指定函数为内联函数,有的编译系统也会自动将很简单的函数作为内联函数处理;而对于复杂的函数,即便你指定他为内联函数,系统也不会理会的。

介绍内联函数之前,有必要介绍一下预处理宏。内联函数的功能和预处理宏的功能相似。相信大家都用过预处理宏,我们会经常定义一些宏,如

#define TABLE_COMP(x) ((x)》0?(x):0)

就定义了一个宏。

为什么要使用宏呢?因为函数的调用必须要将程序执行的顺序转移到函数所存放在内存中的某个地址,将函数的程序内容执行完后,再返回到转去执行该函数前的地方。这种转移操作要求在转去执行前要保存现场并记忆执行的地址,转回后要恢复现场,并按原来保存地址继续执行。因此,函数调用要有一定的时间和空间方面的开销,于是将影响其效率。而宏只是在预处理的地方把代码展开,不需要额外的空间和时间方面的开销,所以调用一个宏比调用一个函数更有效率。

但是宏也有很多的不尽人意的地方。

1、.宏不能访问对象的私有成员。

2、.宏的定义很容易产生二意性。

你可能感兴趣的:(C++)