目录
正文开始:
(一)内联函数
宏定义:
C++的内联函数是在C语言宏的基础上提出的,C语言宏是一种预处理器指令,用于在编译过程中进行文本替换。宏定义可以用来定义常量、函数、条件语句等,它们在程序编译之前被 替换 为相应的代码。
宏定义使用#define关键字,语法格式为:
#define 宏名 字符串
宏名通常使用大写字母表示,字符串可以是任意有效的C语言代码。宏定义的作用范围为宏定义之后直到文件末尾或者遇到 #undef 指令。 宏名称在程序中会被替换为对应的字符串,这个替换过程是在编译之前由预处理器完成的。
宏函数:
宏定义还可以带有参数,类似于函数,可以根据参数的不同生成不同的代码。宏参数使用括号括起来,并且在宏定义中用逗号隔开。例如:
#define MAX(x, y) ((x) > (y) ? (x) : (y))
这个宏定义了一个求两个数中较大值的函数,它可以像函数一样调用:
int max = MAX(a, b);
在编译过程中,预处理器会将MAX(a, b)替换为((a) > (b) ? (a) : (b)),然后再进行编译。
条件宏:
宏定义还可以使用条件语句,例如:
#define DEBUG #ifdef DEBUG printf("Debugging is enabled\n"); #endif
在编译过程中,如果宏定义了DEBUG,那么条件编译指令#ifdef DEBUG和#endif之间的代码会被编译,否则会被忽略。
宏的缺点是非常明显的,以加法宏函数为例,想要正确写出宏函数,需要考虑:
#define ADD(x,y) ((x)+(y))
(1)为什么要加内层括号?
对于一些情况比如x,y是运算符优先级低于加法运算符优先级的情况,如果不加内层括号,就会在算出x,y结果之前进行+运算。
(2)为什么要加外层括号?
在ADD(8,9)*8情况下,如果不加括号,进行宏替换后就会先运算9*8,而不是先算出ADD的结果再进行运算。
(3)宏定义末尾不能加分号?
在对ADD函数进行嵌套调用的时候,如果加上末尾的“;”,就会出现多余分号的情况。
C语言中的宏定义提供了一种在编译前进行代码替换的机制,它可以提高代码的灵活性和可重用性。但是宏定义也有一些缺点,如代码可读性差(导致调试不方便)、与函数相比没有类型检查(宏做的仅仅是替换),在有些场景下比较复杂(需要谨慎替换后运算符的优先级)等。因此,在使用宏定义时需要谨慎考虑代码的可读性和可维护性。
由于宏的缺点比较明显,于是C++推荐:
i, 用const和enum替代宏常量;
ii,用inline(内联函数)替代宏函数;
C++的替代
宏常量可以牵一发而动全身,提高代码的可维护性;宏函数可以不用建立函数栈帧,提高了效率;
内联函数
//内联函数的写法 inline Add(int x,int y) { return x+y; } int main() { std::cout << Add(6,5) << std::endl; return 0; }
特性:
内联函数是以空间换时间的做法,如果编译器将函数当成内联函数处理,会在编译阶段,用函数体替换函数调用;
缺陷:可能会使编译后的文件(可执行程序)变大;
优点:减少了调用开销,提高了程序运行效率;
内联函数是对编译器的一个建议,对于我们实现的内联函数,编译器不一定执行,不同编译器关于inline函数得实现机制可能不同;一般情况下,建议将函数规模较小,不是递归且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性;
inline函数不要让声明和定义分离,分离会导致链接错误;因为inline被展开,就不再调用函数,没有函数地址了,链接就会找不到。
(二)关键字auto
在C++中,关键字auto用于声明一种自动类型推断的变量。它允许编译器根据变量的初始值来推断变量的类型,并将其类型作为变量的类型。
使用auto关键字可以简化代码,减少类型声明的冗余,并提高代码的可读性。
int a = 10;
auto b = a;//自动推断b的类型为int
auto关键字被用于声明变量b,并根据它们的初始值来推断其类型。
auto x = 10; // x的类型被推断为int
auto str = "Hello"; // str的类型被推断为const char *
auto关键字被用于声明变量x和str,并根据它们的初始值来推断其类型。
迭代器类型推导:
auto关键字可以在C++11及以后的版本中用于自动推导迭代器类型。
std::vector<int> numbers = {1, 2, 3, 4, 5};
for(auto it = numbers.begin(); it != numbers.end(); ++it)
{
std::cout << *it << " ";
}
在上面的例子中,auto关键字用于推导迭代器it的类型,编译器会根据numbers容器的类型推导出it的类型为std::vector<int>::iterator(这样就简化了输入类型的代码)。
auto特性:
需要注意的是,auto关键字在类型推导时会考虑初始化值,所以在使用auto声明变量时,要确保初始化值是明确的,否则推导的结果可能不符合预期。
auto不能作为函数形参的类型推导;
auto不能直接用来声明数组;
在一行声明多个变量时,变量的推断类型必须一致;
auto a = 5,b = 6; //推断为int auto a = 5,b = 6.0; //推断声明失败
(三)范围for
C++11中新增的语法,便利数组,新增了范围for的用法:
int arr[] = {1,2,3,4,5};
for(auto i : arr )
{
cout << i << endl;
}
int arr[] = {1,2,3,4,5};
for(int i = 0; i < sizeof(arr)/sizeof(int);i++ )
{
cout << arr[i] << endl;
}
上述两种遍历数组的结果是一样的;
对于范围for,编译器自动识别数组中的数据类型并赋值给i对象,自动判断结束;
范围for特性:
e的改变并不会改变数组元素的值:
int arr[] = {1,2,3,4,5}; for(auto i : arr)//e的改变并不影响数组的元素的值 { e*=2; cout << e << endl; }
不能在数组传参的函数内使用,数组传参本质上是传递数组首元素的地址;
(四)nullptr
C++中如果NULL作为函数参数进行函数重载,会被默认识别成 整型0 ,但是NULL本质上是(void*)0 ;
于是C++11打了一个补丁,用nullptr替代NULL,nullptr会被识别为(void*)0;
nullptr是一个关键字;
(为了向前兼容,C++没有直接将NULL的定义改为(void*)0)
完~
未经作者同意禁止转载