文章目录
- 1. 内联函数❤️
- 1.1 内联函数的定义
- 1.2 特性
- 2. auto关键字🤍
- 2.1 auto简介
- 2.2 auto使用细则
- 2.3 auto不能进行推导的场景
- 3. 基于范围的for循环🧡
- 3.2 范围for的条件
- 4. nullptr指针💚
1. 内联函数❤️
在C语言中有宏函数的概念,宏函数是宏的一种,可以实现和函数一样的功能
宏函数相比函数的优点有:
- 运行速度更快,不需要开辟栈帧
- 宏不需要指定参数类型
缺点:
3. 没有函数安全,不会进行类型检查
4. 无法调试
5. 可读性不高
C++中引入了内联函数保留了函数的优点又继承了宏的优点
1.1 内联函数的定义
以inline
修饰的函数叫做内联函数,编译阶段
时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,内联函数提升程序运行的效率
下面代码说明了内联函数可以直接展开
//内联函数
inline void fun1()
{
int a = 1;
int b = 2;
int c = a + b;
}
int main()
{
fun1();
return 0;
}
汇编代码
程序执行fun1函数时,汇编指令并没有call
指令,说明了fun1函数是直接被展开的
想要观察内联函数的汇编指令需要按如下方式设置(下面给出vs2022的方式):
首先打开 项目->属性
1.2 特性
-
inline
是使用空间换时间的一种做法,如果函数体太大,用函数体替换函数调用后会使目标文件变大。 -
inline
对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建
议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不
是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。
-
inline
不建议声明和定义分开,分开会导致链接错误。因为调用内联函数时会将该函数展开,但是声明和定义是分开的,所以在调用的文件中找不到内联函数的函数体,会发生链接错误.声明时用extern
关键字可以将内联函数定义和声明分开
2. auto关键字🤍
在编程时,常常需要把表达式的值赋值给变量,这就要求在声明变量的时候清楚地知道表达式的
类型。然而有时候要做到这点并非那么容易,因此C++11给auto赋予了新的含义。
2.1 auto简介
在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,但遗憾的
是一直没有人去使用它
C++11中,标准委员会赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一
个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。
int TestAuto()
{
return 10;
}
struct Student
{
string name;
int age;
char sex;
};
int main()
{
int a = 10;
auto b = a;
auto c = 'a';
auto d = TestAuto();
auto f = 1.2;
cout << typeid(b).name() << endl;
cout << typeid(c).name() << endl;
cout << typeid(d).name() << endl;
cout << typeid(f).name() << endl;
struct Student s1 = { "sy", 18, 'M' };
auto s = s1;
cout << typeid(s).name() << endl;
//auto e; 无法通过编译,使用auto定义变量时必须对其进行初始化
return 0;
}
2.2 auto使用细则
-
auto与指针和引用结合起来使用
用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须
加&int main() { int a = 10; int b = 20; int c = 30; auto pa = &a; auto* pb = &b; auto ppa = &pa; auto* ppb = &pb; auto& rc = c; cout << typeid(pa).name() << endl; cout << typeid(pb).name() << endl; cout << typeid(ppb).name() << endl; cout << typeid(ppb).name() << endl; cout << typeid(rc).name() << endl; return 0; }
-
在同一行定义多个变量当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
2.3 auto不能进行推导的场景
-
auto不能作为函数的参数
// 此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导 void TestAuto(auto a) {}
-
auto不能直接用来声明数组
//auto不能使用的场景 int main() { int a[] = { 1, 2, 3, 4 }; auto b[] = {1, 2, 3, 4}; cout << typeid(b).name() << endl; return 0; }
-
为了避免与C++98中的auto发生混淆,C++11只保留了auto作为类型指示符的用法
-
auto在实际中最常见的优势用法就是跟以后会讲到的C++11提供的新式for循环,还有lambda表达式等进行配合使用。
3. 基于范围的for循环🧡
在C++11之前想要遍历一个数组必须将数组元素的个数求出来
int main()
{
int arr[] = { 1, 2, 3, 4, 5 };
for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
cout << arr[i] << " ";
return 0;
}
对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还容易出错,因此C++11引入了基于范围的for循环。for循环由:前后的两部分组成,:前面的是在范围内迭代的变量,:后面的是迭代的范围
int main()
{
int arr[] = { 1, 2 ,3 ,4 };
for (auto e : arr)//e是迭代变量,arr是一个数组,在这个数组中进行迭代
cout << e << " ";
cout << endl;
for (auto& e : arr)
e *= 2;
for (auto e : arr)
cout << e << " ";
return 0;
}
3.2 范围for的条件
- for循环迭代的范围必须是确定的
对于数组而言,就是数组中第一个元素和最后一个元素的范围;对于类而言,应该提供
begin和end的方法,begin和end就是for循环迭代的范围。
注意:以下代码就有问题,因为for的范围不确定
void TestFor(int array[])
{
for(auto& e : array)
cout<< e <<endl;
}
- 迭代的对象要实现++和==的操作。(关于迭代器这个问题,以后会讲,现在提一下,没办法
讲清楚,现在大家了解一下就可以了)
4. nullptr指针💚
NULL的定义如下
C语言中的NULL指针定义为#define NULL ((void*)0)
C++中的NULL指针定义为#define NULL 0
如果在C++中像使用NULL指针,因为它被字面量0替换,所以在某些情况下会出现问题
NULL指针并没有发挥出指针的功能,如果像让NULL在C++中发挥出空指针的效果,那么需要强制类型转换为void*,C++11为了解决这个问题,引入了关键字nullptr