【Lesson 3.内联函数和auto关键字和指针空值nullptr】

本文详细介绍了C++11中的内联函数,包括其概念、特性和使用注意事项;讲解了auto关键字的作用,如自动类型推导,并展示了其在不同场景下的应用限制;探讨了范围for循环的语法和使用条件;最后讨论了C++11引入的指针空值nullptr及其相对于旧版C++的优势。
摘要由CSDN通过智能技术生成

@C++

1. 内联函数

a. 概念

‘为了解决一些多次调用但是代码量小的函数引起的大量消耗栈空间(内存)的问题’
1.以inline修饰的函数叫内联函数。编译时C++编译器会在调用内联函数的地方展开,没有函数压栈的开销

inline int Add(int left, int right)
{
	return left + right;
}

int main()
{
	int a = 10;
	int b = 20;
	int ret = Add(a, b);//查看反汇编,debug和release版本
	return 0;
}

在release模式下,编译器生成的汇编代码不存在call Add

	int a = 10;
	int b = 20;
	int ret = Add(a, b);//查看反汇编,debug和release版本
	return 0;  

在debug模式下,编译器生成的汇编代码存在call Add
因为在的debug模式下,需要供程序员进行调试,编译器默认不会对代码进行优化

	int a = 10;
00007FF7354720AB  mov         dword ptr [a],0Ah  
	int b = 20;
00007FF7354720B2  mov         dword ptr [b],14h  
	int ret = Add(a, b);//查看反汇编,debug和release版本
00007FF7354720B9  mov         edx,dword ptr [b]  
00007FF7354720BC  mov         ecx,dword ptr [a]  
00007FF7354720BF  call        Add (07FF73547147Eh)  
00007FF7354720C4  mov         dword ptr [ret],eax  
	return 0;
  1. 在debug模式下,编译器一般是不会将内联函数直接展开,因为在debug模式下,用户要调试代码。

b. 特性

i. inline是一种以空间换时间的做法

以代码复制为代价,仅仅省去函数调用的开销,从而提高函数执行效率。所以=代码很长或者有循环/递归的函数,复制的消耗比调用消耗还大,那么就不适宜使用作为内联函数
(1)函数内代码很长,使用内联将导致函数消耗代价过高
(2)如果函数体内出现循环/递归,那麽执行函数体内代码的时间要比函数调用开销大

ii. inline对于编译器而言只是一个建议

我们声明了内联函数,编译器会自动优化,定义为inline的函数体内有循环/递归等,函数较为复杂编译器会忽略掉内联,如果调用函数不复杂,则在调用的地方展开,执行内联函数。

iii. inline不建议声明和定义分离

分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。

2. auto关键字(C++11)

a. auto简介

i. auto不再是一个存储类型指示符。而是作为一个新的类型指示符来指示编译器,auto声明的变量必须有编译器在编译时期推导得。

int main()
{
   auto a = 10;//在写代码时,auto就是一个占位符
   auto b = 12.3;
   auto c = 'c';
   cout << typeid(a).name()<< endl;
   cout << typeid(b).name()<< endl;
   cout << typeid(c).name() << endl;
   return 0;
}
int
double
char

注:使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要=根据初始化表达式来推导auto的实际类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会将auto替换为变量的实际类型。

b. auto的使用细则

i. auto与指针和引用结合起来使用

int a=12;
auto* pa=&a;//auto声明指针变量时auto和auto*没有区别
auto pa=&a;

auto& ra=a;//auto生命引用类型时则必须要加上&

ii. 在同一行定义多个变量

int main()
{
	auto a = 12, b = 34, c = 56;
	auto a = 12, b = 1.2, c = 'c';//编译失败
	return 0;
}

为什么声明同一行变量时,变量应为同一类型?
因为编译器实际上只会对第一个类型进行推导,然后用推到的类型定义其他变量

c. auto不能推导的场景

i. auto不能作为函数的参数

ii. auto不能直接用来声明数组

3. 基于范围的for循环(C++11)

a. 范围for的语法

对于一个有范围的数组,由程序员说明范围是多余的。
因此C++11中,引进了 基于范围的for循环

int main()
{
	int array[] = { 1,2,3,4,5,6,7,8,9,0 };
	//e就是array数组的每个元素的一份拷贝
	for (auto e : array)//C++范围for循环
		cout << e << " ";
	cout << endl;
	/*
	int* p = array; //无法使用指针表示数组被迭代的范围
	for (auto e : p)//int *的范围是不具体的的——编译器编译阶段无法确定p所表示的范围
	*/
	//e就是array每个元素的别名
	for (auto& e : array)
		cout << e << " ";
	cout << endl;
	return 0;
}
1 2 3 4 5 6 7 8 9 0
1 2 3 4 5 6 7 8 9 0

b. 范围for的使用条件

范围for迭代的范围必须是确定的,如果使用指针代替数组,那么就无法确定其迭代范围。(上述代码)

4. 指针空值nullptr

C++98中的指针空值

NULL实际就是一个宏,在传统的c头文件中<stdio.h>

#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif

可以知道,NULL可能被定义为常量0,也可以被定义为无类型指针(void*)的常量

void Test(int* a)
{
	cout << "Test(int* a)" << endl;
}
void Test(int a)
{
	cout << "Test(int a)" << endl;
}
int main()
{
	int a = 1;
	int* b = &a;
	int* pd = NULL;//NULL表示空指针
	Test(a);
	Test(b);
	Test(pd);
	Test(NULL);
	Test((int*)NULL);//NULL表示空指针,应该调用Test(int*
	Test(0);

	int* pc = nullptr;//空值指针:(void*)0的指针
	Test(pc);
	Test(nullptr);
	return 0;
}
Test(int a)
Test(int* a)
Test(int* a)
Test(int a)
Test(int* a)
Test(int a)
Test(int* a)
Test(int* a)

通过f(NULL)调用指针版本的f((int *))函数,,但是由于NULL被定义为了0,与程序相违背。
而在C++11中nullptr表示指针空值(void *)0的指针。

注意

  1. 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入的。
  2. 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。
  3. 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值