目录
1.auto关键字(C++11)
1.1 typedef取别名的缺陷
1.2 auto
1.3 auto使用细则
1.3.1 auto与指针引用结合使用
1.3.2 同一行定义多个变量
1.4 auto不能推导的场景
1.4.1 auto不能作为函数的形参
1.4.2 auto不能直接用来声明数组
2. 内联函数
2.1 宏的缺陷
2.2 内联函数概念
2.3 内联函数特性
3.nullptr关键字
在敲代码时往往会遇到一个比较长的类型名,这时候通常会使用typedef对该类型进行重命名,但有一种情况typedef会有一定的缺陷
typedef char* pstring;
int main()
{
const pstring p1;
const pstring* p2;
return 0;
}
在这段代码中,第一个定义是编译不过的,因为第一个类型声明展开后是
char* const p1
并不是
const char* p1
因此定义p1就必须初始化(const修饰p1,意味着p1是一个常量指针,必须初始化),否则编译报错。确实这种展开看起来很怪,但是语法如此。
C++11中,auto可以自动识别类型
int main()
{
const int a = 1;
auto b = a;
b = 2;
auto& ra = a;
return 0;
}
在面对很长的类型名时,这种特性就比较实用。
auto可以自动识别指针类型变量
int main()
{
int a = 0;
auto pa1 = &a;
auto* pa2 = &a;
return 0;
}
pa1和pa2没有区别。
auto*意为必须为一个指针类型的变量。
auto识别引用则必须要加上&
int main()
{
int a = 0;
auto& ra = a;
return 0;
}
这种情况下一行中的变量必须是同一个类型,否则会报错。
注意:auto使用时必须初始化,因为编译器需要根据初始化的数据类型确定出auto的实际类型。
void func(auto a)
{
;
}
此处编译器会报错,因为编译器无法推导出a的实际类型 。
void TestAuto()
{
int a[] = {1,2,3};
auto b[] = {4,5,6};
}
在C语言中,宏可以定义一些短小简单的功能,比如下面这个求两个数较大值的宏
#define Max(a, b) a > b ? a: b
但由于宏只是做简单的替换工作,导致宏比较不安全,并且非常容易写错,比如上面的那个宏就写的不好。 因为传给a的可能是表达式,表达式中可能有各种运算符,就可能打乱运算顺序,导致最终得不到像要的结果,要解决这个问题就得加上很多括号。
#define Max(a, b) ((a) > (b) ? (a): (b))
这样才算得上比较完善。
从这也能看出宏的缺点:
不能调试
没有安全的类型检查
过于复杂
尽管宏有那些缺点,但是宏却有一个不折不扣的好处,由于是直接在编译阶段就展开了,省去了函数调用建立栈帧的过程,效率会更高。为了能够继承宏的好处,同时能够丢弃宏的缺点,C++提出了内敛函数。
以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调
用建立栈帧的开销,内联函数提升程序运行的效率。
inline int Add(int x, int y)
{
int c = x + y;
return c;
}
编译期间函数会直接用函数体替换函数调用。
查看方式:
1. 在release模式下,查看编译器生成的汇编代码中是否存在call Add
2. 在debug模式下,需要对编译器进行设置,否则不会展开(因为debug模式下,编译器默认不
会对代码进行优化,以下给出vs2022的设置方式)
1.内联函数是一种用空间换时间的方式,因为内联函数直接展开,因此会增加代码量,在工程中如果有大量的内联函数展开,会极大的影响文件的大小,一个五十行的函数,如果被使用了一百次,就会多出五千行代码,因此只有在函数较短时才会使用内联。
2.inline对于编译器只是一个建议,也就是说编译器不一定会直接展开,一般建议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。
3.inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址
了,链接就会找不到。
NULL实际上是一个宏,但是在C++的定义中,NULL出了一点问题
这是C++中对于NULL 的定义,不知道为什么NULL被直接定义成了0,导致在使用时会出现一些问题,因此C++11标准中打了一个补丁,就是nullptr。
nullptr并非是宏,而是一个关键字,NULL需要包含头文件才能使用,而nullptr不需要。