所有运行结果均在VS2022,Debug,x86的环境下得出
一、引用与指针的区别
1. 地址存储方面
- 语法上:引用不开空间,指针开辟空间
- 底层上:引用和指针都会开辟空间
2.空值
- 引用没有NULL引用
- 指针有NULL指针
int main()
{
int* ptr = NULL;
int& r = *ptr;
return 0;
}
这是一段特殊的代码
3.在sizeof中的含义不同
- 引用的结果是引用类型的大小
- 指针的结果是地址空间所占字节个数
int main()
{
int a = 1;
double m = 5.20;
double& n = m;
int& b = a;
cout << sizeof(b) << endl;
cout << sizeof(n) << endl;
cout << sizeof(int&) << endl;
cout << sizeof(double&) << endl;
return 0;
}
4.其他不同点
-
引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
-
有多级指针,但是没有多级引用
-
访问实体方式不同,指针需要显式解引用,引用编译器自己处理
-
引用比指针使用起来相对更安全
二、内联函数
1.概念
以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,内联函数提升程序运行的效率。
下面是一个内联函数ADD:
inline int ADD(int x, int y)
{
int z = x + y;
cout << z << endl;
return x + y;
}
int main()
{
int c = ADD(1, 2);
return 0;
}
2.特性
2.1以空间换时间的一个做法
- inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用
- 缺陷:可能会使目标文件变大
- 优势:少了调用开销,提高程序运行效率。
2.2对编译器而言,只是一个建议
- inline对于编译器而言只是一个建议
- 不同编译器关于inline实现机制可能不同
- 一般建议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性
2.3不建议声明和定义分离
- inline不建议声明和定义分离,分离会导致链接错误。
- 因为inline被展开,就没有函数地址了,链接就会找不到
看下面代码示例:
// F.h
#include <iostream>
using namespace std;
inline void f(int i);
// F.cpp
#include "F.h"
void f(int i)
{
cout << i << endl;
}
// main.cpp
#include "F.h"
int main()
{
f(10);
return 0;
}
2.4合适场景可以替换宏
- 我们知道宏有以下特点:
-
- 优点:
1.增强代码的复用性。
2.提高性能。
- 优点:
-
- 缺点:
1.不方便调试宏。(因为预编译阶段进行了替换)
2.导致代码可读性差,可维护性差,容易误用。
3.没有类型安全的检查 。
- 缺点:
-
经过大家的不断努力,我们发现:
- C++有这些技术可以替代宏?
- 常量定义 换用const enum
- 短小函数定义 换用内联函数
三、auto关键字 (C++11)
1.auto简介
- auto不再是一个存储类型指示符,而是作为一
个新的类型
2.auto使用场景
2.1做类型声明“占位符”
看如下代码示例:
#include<iostream>
#include <typeinfo>
#include<stdio.h>
using namespace std;
int main()
{
auto a = 2;
//int a=2;
auto b = 3.14;
//double b=3.14;
auto a1 = &a;
auto* b1 = &b;
auto& b2 = b;
printf("%s\n", typeid(a).name());
printf("%s\n", typeid(b).name());
printf("%s\n", typeid(a1).name());
printf("%s\n", typeid(b1).name());
printf("%s\n", typeid(b2).name());
return 0;
}
这里,我们将不同变量的类型给打印了出来
我们还可以得出以下结论:
- 当auto与指针结合使用时,可以写成以下两种形式
auto a1 = &a;
auto* b1 = &b;
- 当auto与引用 结合时,可以写成以下形式
auto& b2 = b;
2.2在同一行定义多个变量
代码示例如下:
int main()
{
auto a = 1, b = 2, c = 3;
auto x = 1.1, y = 1.2, z = 1.3;
cout << "a=" << a << endl;
cout << "b=" << b << endl;
cout << "c=" << c << endl;
cout << "x=" << x << endl;
cout << "y=" << y << endl;
cout << "z=" << z << endl;
return 0;
}
但是仍然需要注意的是,同一行定义的多个变量必须是相同类型的,读者们可自行试试不同类型的,这里笔者不做代码展示。
3.auto不能用的场景
3.1auto不能作为函数形参的类型
代码示例如下:
int ADD(auto a,auto b)
{
cout << a + b << endl;
return a + b;
}
这里的报错为:
此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导
3.2 auto不能用来直接声明数组
代码示例如下:
int main()
{
int a1[] = { 1,2,3,4,5 };
auto a2[] = { 1,2,3,4,5 };
return 0;
}
报错如下:
可见,不可这样声明数组。
4.auto与范围for循环结合使用(遍历数组)
4.1常规使用
代码示例如下:
这里的e是一个参数,也可以写其他的。
int main()
{
int array[] = { 1,2,3,4,5 };
for (auto e : array)
{
cout << e << " ";
}
cout << endl;
return 0;
}
这里是最简单的auto与范围for循环的使用方式,它俩结合使用,使数组的遍历更简单更方便。
这个范围for循环几乎没有什么缺点,但是并不能倒着遍历数组。
4.2改变数组的遍历数组的方式
代码示例如下:
int main()
{
int array[] = { 1,2,3,4,5 };
for (auto& e : array)
{
e = e * 2;
}
for (auto e : array)
{
cout << e << " ";
}
cout << endl;
return 0;
}
这里的数组变成了原来的两倍
仅仅是添加了以下代码片段,对e进行了引用和调整,改变了原先的数组
for (auto& e : array)
{
e = e * 2;
}
四、指针空值nullptr(C++11)
1.引入
看下列代码及运行结果:
void f(int)
{
cout << "f(int)" << endl;
}
void f(int*)
{
cout << "f(int*)" << endl;
}
int main()
{
f(0);
f(NULL);
return 0;
}
发现其结果和自己预想的并不一样。
这时,我们引入了nullptr(C++11) 。
2.nullptr使用
看以下改进的代码及结果:
void f(int)
{
cout << "f(int)" << endl;
}
void f(int*)
{
cout << "f(int*)" << endl;
}
int main()
{
f(0);
f(nullptr);
return 0;
}
这里使用了nullptr,得到了原代码想要的结果。
3.nullptr使用说明
-
- 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入的。
-
- 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。
-
- 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。