目录
一、内联函数
1、概念
以
inline
修饰
的函数叫做内联函数,
编译时
C++
编译器会在
调用内联函数的地方展开
,没有函数调
用建立栈帧的开销,内联函数提升程序运行的效率。
函数会建立栈帧:
int Add(int x, int y)
{
return x + y;
}
int main()
{
int ret = 0;
ret = Add(1, 2);
cout << ret << endl;
return 0;
}
在
调用内联函数的地方展开
,没有函数调用建立栈帧的开销
inline int Add(int x, int y)
{
return x + y;
}
int main()
{
cout << Add(1, 2) << endl;
return 0;
}
2、特性
1. inline
是一种
以空间换时间
的做法,如果编译器将函数当成内联函数处理,在
编译阶段,会
用函数体替换函数调用
,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运
行效率。
不会建立栈帧,但会使目标文件扩大
inline int Add(int x, int y)
{
return x + y;
}
int main()
{
cout << Add(1, 2) << endl;
return 0;
}
2.
inline
对于编译器而言只是一个建议,不同编译器关于
inline
实现机制可能不同
,一般建
议:将
函数规模较小
(
即函数不是很长,具体没有准确的说法,取决于编译器内部实现
)
、
不
是递归、且频繁调用
的函数采用
inline
修饰,否则编译器会忽略
inline
特性。
编译器会忽视inline特性
inline int Func()
{
int ret = 0;
int x1 = 0;
int x2 = 0;
int x3 = 0;
int x4 = 0;
int x5 = 0;
int x6 = 0;
ret += x1;
ret *= x1;
ret *= x2;
ret /= x3;
ret /= x5;
return ret;
}
int main()
{
int ret = Func();
cout << ret << endl;
return 0;
}
3、inline
不建议声明和定义分离,分离会导致链接错误。因为
inline
被展开,就没有函数地址了,链接就会找不到。
//声明和定义分离,导致链接错误
//Func.h
#include<iostream>
using namespace std;
inline int Add(int x, int y);//函数声明
//Func.cpp
#include"Func.h"
inline int Add(int x, int y)//函数定义
{
return x + y;
}
//test.cpp
#include"Func.h"
int main()
{
cout << "Add(1,2)" << "=" << Add(1, 2) << endl;
return 0;
}
代码结果:链接出错
二、auto关键字
1、auto简介
auto关键字能够让编译器通过初始化的表达来推导变量的类型。
在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,但遗憾的 是一直没有人去使用它。C++11中,标准委员会赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一 个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得
int Test()
{
return 6;
}
int main()
{
int a = 1;
auto b = a;
auto c = &a;
double h = 2.222;
auto i = h;
//auto w;//错误示范,必须初始化
char j = 'c';
auto k = j;
auto m = Test();
//打印变量类型
cout << typeid(b).name() << " ";//int类型
cout << typeid(c).name() << " ";//int* 类型
cout << typeid(i).name() << " ";//double类型
cout << typeid(j).name() << " ";//char类型
cout << typeid(k).name() << " ";//char类型
cout << typeid(m).name() << " ";//int类型
return 0;
}
【注意】
使用
auto
定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导
auto
的实际类型
。因此
auto
并非是一种
“
类型
”
的声明,而是一个类型声明时的
“
占位符
”
,编译器在编
译期会将
auto
替换为变量实际的类型
。
2、auto使用细则
1.
auto
与指针和引用结合起来使用 。
用
auto
声明指针类型时,用
auto
和
auto*
没有任何区别,但用
auto
声明引用类型时则必须
加
&。
int main()
{
int x = 10;
auto a = &x;//自动推导出int*类型
auto* b = &x;//自动推导出int*类型
auto& c = x;//自动推导出int类型
cout << typeid(a).name() << endl;//打印结果为int*
cout << typeid(b).name() << endl;//打印结果为int*
cout << typeid(c).name() << endl;//打印结果为int
return 0;
}
2.
在同一行定义多个变量
当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译
器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量
。
int main()
{
auto a = 1, b = 2;//正确,都是int类型
auto c = 2.22, d = 3;//错误,auto的声明的同一行,必须是相同类型
}
3、auto不能推到的场景
auto不能作为函数的参数
void Test(auto a)// 此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导
{
//...//
}
auto不能直接用来声明数组
void Test()
{
int a[] = { 1,2,3 };
auto b[] = { 4,5,6 };//此处编译失败,auto不能直接用来声明数组
}
三、基于范围的for循环(C++11)
1、范围for的语法
在
C++98
中如果要遍历一个数组,可以按照以下方式进行:
int main()
{
int array[] = { 1,2,3,4,5,6 };
//让数组中的元素都分别乘以2
for (int i = 0; i < sizeof(array) / sizeof(int); ++i)
{
array[i] *= 2;
}
//分别打印数组中的元素
for (int i = 0; i < sizeof(array) / sizeof(int); ++i)
{
cout << array[i] << " ";//打印的结果为2,4,6,8,10,12;
}
cout << endl;//换行
return 0;
}
对于一个
有范围的集合
而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因
此
C++11
中引入了基于范围的
for
循环。
for
循环后的括号由冒号
“
:
”
分为两部分:第一部分是范
围内用于迭代的变量,第二部分则表示被迭代的范围。
所以上述遍历数组的代码可以改为
int main()
{
int array[] = { 1,2,3,4,5,6 };
//让数组中的元素都分别乘以2
for (auto& e : array)//要引用,因为修改e要对array数组产生影响。不引用,修改e不会改变array数组
{
e *= 2;
}
//分别打印数组中的元素
for (auto e : array)
{
cout << e << " ";
}
cout << endl;//换行
return 0;
}
注意:与普通循环类似,可以用
continue
来结束本次循环,也可以用
break
来跳出整个循环
。
2、范围for的使用条件
1、for
循环迭代的范围必须是确定的
对于数组而言,就是数组中第一个元素和最后一个元素的范围
;对于类而言,应该提供
begin
和
end
的方法,
begin
和
end
就是
for
循环迭代的范围。
注意:以下代码就有问题,因为
for
的范围不确定
void TestFor(int array[])//这个数组没有范围
{
for (auto& e : array)//array范围不确定
cout << e << endl;
}
2.
迭代的对象要实现
++
和
==
的操作
。
(关于迭代器这个问题,现在大家了解一下就可以了
)
四、指针空值nullptr(C++11)
NULL被定义为0
void f(int)
{
cout << "f(int)" << endl;
}
void f(int*)
{
cout << "f(int*)" << endl;
}
int main()
{
f(0);//打印的结果是int
f(NULL);//打印的结果是int
f((int*)NULL);//打印的结果是int*
return 0;
}
程序本意是想通过
f(NULL)
调用指针版本的
f(int*)
函数,但是由于
NULL
被定义成
0
,因此与程序的
初衷相悖。
在
C++98
中,字面常量
0
既可以是一个整形数字,也可以是无类型的指针
(void*)
常量,但是编译器
默认情况下将其看成是一个整形常量,如果要将其按照指针方式来使用,必须对其进行强转
(void
*)0。
注意:
1.
在使用
nullptr
表示指针空值时,不需要包含头文件,因为
nullptr
是
C++11
作为新关键字引入
的
。
2.
在
C++11
中,
sizeof(nullptr)
与
sizeof((void*)0)
所占的字节数相同。
3.
为了提高代码的健壮性,在后续表示指针空值时建议最好使用
nullptr
。