004 C++基础篇
前言
大家好,本文将会为您带来内联函数,auto关键字,基于范围的for循环,指针空值nullptr相关知识一、内联函数
什么是内联函数
以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,提升程序运行的效率。
内联函数的运用
inline int Add(int a, int b)
{
return a + b;
}
int main()
{
int ret = 0;
ret = Add(1, 2);
return 0;
}
在观察有无inline的差异时,我们首先应做以下配置(在Debug模式下)
鼠标右键点击项目
点击属性
以下是程序加上inline与不加inline的反汇编(待程序调试起来,鼠标右键点击反汇编)
不加inline
加inline
我们可以观察到不加inline会有call语句去调用函数,而加inline后call语句则被替换成了函数体(mov,add,mov)
inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用
缺陷:会使目标空间变大,优势:少了调用开销,提高程序运行效率
内联函数的局限性
1、内联机制用于规模较小,流程直接、频繁调用的函数。很多编译器都不支持内联递归函数,当一个规模较大的函数可能将不会在调用点内联地展开
2、inline不建议声名与定义分离
以下程序如果调用f(),则会出现错误
func.cpp
#include "Func.h"
void f(int i)
{
cout << i << endl;
}
void fx()
{
f(1); //在此文件里有f的声名和定义,不用call f()地址,直接展开
}
func.h
#include <iostream>
using namespace std;
inline void f(int i);
main.cpp
int main()
{
error:f(1);//此语句虽然是内联,但是展不开,因为.h文件里只有声名
//只能去链接,但是内联函数不生成指令进入符号表。符号表里没有该函数的地址,如果声名与定义分离,就会出现链接错误
fx();//如果调用fx,便可以间接展开f的内联,原因是在func.cpp中,f函数的声名(头文件)和定义都存在
return 0;
}
二、auto关键字( C++11 )
在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量 在C++11及之后的版本中,auto关键字被重新定义为类型推断的工具。使用auto可以让编译器根据变量的初始化表达式来推断其数据类型,从而省略了显式指定变量类型的过程。注意:用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&
int main()
{
int x = 1;
auto& a = x;
auto b = 1.1;
auto c = 'c';
auto d = &a;
auto* e = &a;
cout << a << endl;
cout << b << endl;
cout << c << endl;
cout << d << endl;
cout << e << endl;
return 0;
}
auto不能推导的场景
1、当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
void TestAuto()
{
auto a = 1, b = 2;
auto c = 3, d = 4.0; // 该行代码会编译失败,因为c和d的初始化表达式类型不同
}
2、auto不能作为函数的参数
void TestAuto(auto a)
{}
// 此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导
3、auto不能直接用来声明数组
void TestAuto()
{
int a[] = {1,2,3};
auto b[] = {4,5,6};
}
三、基于范围的for循环( C++11 )
如果要遍历一个数组,对于程序员来说,说明循环的范围是没有太大必要的,因此C++11中引入了基于范围的for循环。
由两部分组成:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围
int main()
{
int array[] = { 1,2,3,4,5,6,7,8,9 };
for (auto a : array)
{
cout << a << " ";
}
return 0;
}
四、指针空值nullptr( C++11 )
在C++98中,我们通常使用NULL来表示空指针 NULL实际上是一个宏,在传统的C头文件(stddef.h)中,如下:#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
可以看到,NULL可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量。不论采取何种定义,在使用空值的指针时,都不可避免的会遇到一些麻烦
NULL是一个宏定义,其值为0,因此可以被隐式地转换为整型。例如在重载函数时,如果有一个函数接受指针参数,另一个函数接受整型参数,传入NULL时可能会导致调用错误的函数
在C++11中,引入了nullptr关键字,用于表示空指针常量。nullptr是一个字面量,它的类型是nullptr_t。nullptr的引入主要是为了解决NULL的一些问题和不明确性。
注意:
1、 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入的。
2、 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。
3、 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。