C++入门(2)—— 内联函数、auto关键字、基于范围for循环、nullptr

目录

  • 1.内联函数
    • 1.1内联函数的概念
    • 1.2内联函数的使用
    • 1.3内联函数的特性
  • 2.auto关键字
    • 2.1auto简介
    • 2.2auto的使用场景
    • 2.3auto不能被使用的场景
  • 3.基于范围for循环
    • 3.1范围for语法
    • 3.2范围for的使用
  • 4.nullptr
    • 4.1nullptr概念

1.内联函数

1.1内联函数的概念

inline修饰的函数叫做内联函数,在编译期间编译器会用函数体替换函数的调用,没有函数调用建立栈帧的开销,提升程序运行的效率。

当我们使用宏来进行加法运算时:

#include 
using namespace std;

#define ADD(x,y) ((x)+(y))
int main()
{
	cout << ADD(1, 2) << endl;
	return 0;
}

可以看到当我要进行加法运算时,并没有调用函数,而是通过宏的形式实现文本替换。

如果函数不加inline修饰,那么它就会建立函数栈帧C++入门(2)—— 内联函数、auto关键字、基于范围for循环、nullptr_第1张图片
如果函数加上inline修饰,那么它就不会建立函数栈帧
C++入门(2)—— 内联函数、auto关键字、基于范围for循环、nullptr_第2张图片
注意,在debug环境下,需要对项目进行属性配置才能有效使用inline关键字。
C++入门(2)—— 内联函数、auto关键字、基于范围for循环、nullptr_第3张图片

1.2内联函数的使用

在我们使用C语言手写快排的时候,Hoare法中调用了多次Swap函数。而Swap函数仅有短短的三行,且出现的频率不低,那么这个时候,让Swap被inline修饰是最好的选择。 inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用

inline void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
void QuickSort_Hoare()
{
	int x = 1000;
	//...
	while (x--)
	{
		int left = 3;
		int right = 5;
		Swap(&left, &right);//Swap函数规模小,且被频繁调用
	}
	//...
}

1.3内联函数的特性

  1. inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用,缺陷:可能会使目标文件变大优势:少了调用开销,提高程序运行效率
  2. inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。
  3. 在多文件操作中,inline修饰的函数声明不能单独放在头文件中,必须声明与定义写在一起。否则就会链接报错。

其原因是因为,inline修饰的函数不会被写进符号表(即使编译器忽略了inline的特性),那么要调用此函数的另一目标文件就找不到此函数的地址

总结:
1.内联函数是用函数体替换函数调用的方法来减少栈帧的开销,提升运行效率
2.在编译器看来,inline只是一个建议。具体需要考虑inline修饰的函数的规模与性质
3.多文件操作中,inline修饰的函数声明与定义分离会导致连接错误

2.auto关键字

2.1auto简介

在早期的C/C++程序中,使用auto修饰的变量,是具有自动存储器的局部变量,但是很遗憾,一直没有人去使用它。

在C++11中,标准委员会赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。我们可以将它理解为,自动推导类型。为了与早期的auto定义不发生冲突,在C++11中丢弃了早期的auto定义

int main()
{
	auto a = 10;
	auto b = a;
	auto& c = a;
	cout << typeid(a).name() << endl;
	cout << typeid(b).name() << endl;
	cout << typeid(c).name() << endl;

	auto x = 1.55;
	auto px = &x;
	cout << typeid(x).name() << endl;
	cout << typeid(px).name() << endl;

	//auto z;//未初始化将无法推导z的类型
	return 0;
}

在这里插入图片描述
可以看到,auto修饰的变量能够根据等号右边的类型推导出修饰的变量的类型。并且auto推导出的变量与常用的int、char、double等没有差异,都会在内存中开辟一块对应大小的空间

2.2auto的使用场景

在我们的学习的编程过程中,程序会越来越复杂。就像在C语言中会使用大量的结构体,在C++中会使用大量的类。这就会导致类型复杂,类型名长度太长,在以后使用此类型定义变量时将会是一份不轻松的工作。所以类似于前面描述的情况,我们都可以考虑使用auto关键字自动推导类型。

2.3auto不能被使用的场景

void f(auto x)
{

}
void f(double x)
{

}
int main()
{
	double a = 5;
	f(a);
	return 0;
}

如果我们碰到这种情况,程序会调用哪个函数?所在C++中,使用auto作为函数参数是禁止的。

auto关键字不能直接去声明数组,数组元素的合法类型应当由数组名前面的类型决定,而使用auto则与数组的定义发生冲突。所以在C++中,auto声明数组也是禁止的。

int main()
{
	int arr1[] = { 1,2,3 };
	auto arr2[] = { 1,2,3 };//不能确定数组元素的类型
	return 0;
}

在这里插入图片描述

3.基于范围for循环

3.1范围for语法

在早期的学习中,我们都是这样打印数组的:

int main()
{
	int arr[] = { 123,45,124,56,123,67,76,234,758,234,65,246 };
	for (int i = 0; i < sizeof(arr) / sizeof(int); i++)
	{
		cout << arr[i] << " " ;
	}
	return 0;
}

那么在C++11中,我们可以这么打印数组:

int main()
{
	int arr[] = { 123,45,124,56,123,67,76,234,758,234,65,246 };
	/*for (int i = 0; i < sizeof(arr) / sizeof(int); i++)
	{
		cout << arr[i] << " " ;
	}*/
	for (auto tmp : arr)
	{
		cout << tmp << " ";
	}
	return 0;
}

对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围

我们可以理解为,arr的数组范围被确定了,那么就不需要我们打代码的人再确定循环的范围。将arr的每个元素放入一个迭代变量tmp中,然后语法会自动进行数组的迭代。并且,这种循环与普通循环一样,都可以使continue、break等关键字。

3.2范围for的使用

现在我们只针对数组。数组的范围必须要被确定,也就是第一个元素和最后一个元素的范围。迭代的变量可以是普通变量、指针、引用等。在目前的入门状态,我们仅仅使用普通变量打印,使用引用修改某一些值。

int main()
{
	int arr[] = { 123,45,124,56,123,67,76,234,758,234,65,246 };
	for (auto tmp : arr)
	{
		cout << tmp << " ";
	}
	cout<<endl;
	for (auto& x : arr)
	{
		x *= 2;
	}
	for (auto tmp : arr)
	{
		cout << tmp << " ";
	}
	return 0;
}

在这里插入图片描述

4.nullptr

4.1nullptr概念

事实上在我们看来这就是一个空指针。不过在早期的版本中,NULL是宏定义的,它的值是0。这就是为什么C++11中会出现nullptr这样的空指针,因为NULL会产一些歧义。
C++入门(2)—— 内联函数、auto关键字、基于范围for循环、nullptr_第4张图片

void f(int)
{
	cout << "f(int)" << endl;
}

void f(int*)
{
	cout << "f(int*)" << endl;
}

int main()
{
	f(0);
	f(NULL);
	f(nullptr);
	return 0;
}

我们自然认为NULL是空指针,那么它就是一个指针类型,实际上不是。或许这也是早期的不严谨之处,C++11或许就是为了填这个坑。
C++入门(2)—— 内联函数、auto关键字、基于范围for循环、nullptr_第5张图片

C++11为了去填这个坑,将nullptr变得更加严格,它是void* 类型的 0 。它并不是宏定义,而是一个关键字

你可能感兴趣的:(c++,开发语言,c语言)