目录
1. 引用
1.1 引用概念
1.2 引用特性
1.3 常引用
1.4 使用场景
1.5 传值、传引用效率比较
1.6 引用和指针的区别
2. 内联函数
2.1 概念
2.2 特性
3.auto关键字(C++11)
3.1 类型别名思考
3.2 auto简介
3.3 auto的使用细则
3.4 auto不能推导的场景
4. 基于范围的for循环(C++11)
4.1 范围for的语法
4.2 范围for的使用条件
5. 指针空值nullptr(C++11)
5.1 C++98中的指针空值
❀❀❀没有坚持的努力,本质上并没有多大的意义。
1. 引用
1.1 引用概念
类型& 引用变量名(对象名) = 引用实体(注意:引用类型必须和引用实体是同种类型的)
b叫做a的引用,b也可以叫做a的别名(abcd四个,只要有一个发生变化,其余都发生变化)
应用1:
void Swap(int& a, int& b) { int tmp = a; a = b; b = tmp; }
数据进行交换,可以不用传指针,可以用引用
应用2:
#include <iostream> typedef struct ListNode { int val; struct ListNode* next; }LTNode; void LTPushBack_C(LTNode** pphead, int x) { //C语言,单链表尾插需要传结构体的二级指针,因为需要改变首部地址 } void LTPushBack_CPP(LTNode*& phead, int x) { //C++中,用引用,仅仅需要传结构体地址 } int main() { LTNode* plist = NULL; //初始化 LTPushBack_C(&plist, 1); LTPushBack_CPP(plist, 1); return 0; }
也可以引用指针类型的
注意:
typedef struct ListNode { int val; struct ListNode* next; }LTNode,*PLTNode; void LTPushBack_CPP(LTNode*& phead, int x) { //C++中,用引用,仅仅需要传结构体地址 } //这两个等同 void LTPushBack_CPP(PLTNode& phead, int x) { //C++中,用引用,仅仅需要传结构体地址 }
1.2 引用特性
代码展示:
#include <iostream>
int main()
{
int a = 10;
int& b = a;
int& c = a;
int& d = b;
//一个变量可以多次引用
int& e;//代码运行到这里会报错,因为引用在定义时必须初始化
int m = 2;
b = m;//b在前面已经引用了a,在这里并不是成为m的别名,而是把m的值赋值给b,然后此时abcd的值都是2
return 0;
}
1. 引用在 定义时必须初始化2. 一个变量可以有多个引用3. 引用一旦引用一个实体,再不能引用其他实体
1.3 常引用
const修饰的变量,只能读不能写(这里的权限,指的是读和写)
#include <iostream> int main() { int a = 0; int& b = a;//权限不变 const int c = 2; int& d = c;//这里是错误的,权限不能被放大 const int x = 3; const int& y = x;//这里是可以的,权限不变 int m = 6; const int& n = m;//这里是可以的,权限缩小 return 0; }
取别名原则:对于引用类型,权限只能缩小,不能放大
临时变量具有常性
#include <iostream> int main() { int a = 10; int& b = a; const int& c = 20;//常量也可以取别名 double d = 15.3; int f = d;//在这里,相当于f把自己的整数部分给一个临时变量,临时变量把值赋给f(临时变量具有常性) const int& e = d;//这里的e不是d的引用,而是临时变量的引用 return 0; }
1.4 使用场景
(1)做参数
void Swap(int& a, int& b)
{
int tmp = a;
a = b;
b = tmp;
}
可以不用传指针
(2)做返回值
代码1展示:(传值返回)
#include <iostream> int Count() { int n = 0; n++; return n; }//n出了这个函数就被销毁了,所以是赋值给临时变量的 int main() { int ret = Count(); return 0; }
函数返回过程,把返回的值n给一个临时变量,临时变量的类型就是函数类型(上述代码的int),临时变量再把值赋给主函数的ret。(临时变量即有一个拷贝)
代码2展示:(传引用拷贝)
#include <iostream> int& Count() { static int n = 0;//static不能去掉,如果去掉,就会涉及出现越界问题(因为空间被系统回收) n++; return n; }//返回int&,说明有一个临时引用是int&类型,临时引用是n的别名 int main() { int& ret = Count();//ret是临时引用的别名, return 0; }
没有拷贝,效率高
如果函数返回时,出了函数作用域,如果返回对象还在 ( 还没还给系统 ) ,则可以使用引用返回, 如果已经还给系统了,则必须使用传值返回。(否则会出现越界问题)
注意:
#include <iostream>
int Count()
{
int n = 0;
n++;
return n;
}
int main()
{
const int& ret = Count();//因为是临时变量的别名,临时变量具有常性
return 0;
}
1.5 传值、传引用效率比较
1.6 引用和指针的区别
语法的角度:引用是一个别名,没有额外开空间,指针存储的是地址,需要开一个4/8字节的空间;但是从底层的角度,是一样的方式实现的(汇编代码是一致的)
2. 内联函数
2.1 概念
知识复习:写一个ADD的宏
inline存在的意义:(1)解决宏函数晦涩难懂、容易写错(2)宏不支持调试
优点:(1)debug支持调试(2)不易写错,就是普通函数的写法(3)提升程序的效率
2.2 特性
知识点 :宏的优缺点?优点:1. 增强代码的复用性。2. 提高性能。缺点:1. 不方便调试宏。(因为预编译阶段进行了替换)2. 导致代码可读性差,可维护性差,容易误用。3. 没有类型安全的检查 。C++ 有哪些技术替代宏 ?1. 常量定义 换用 const2. 函数定义 换用内联函数
3.auto关键字(C++11)
3.1 类型别名思考
auto可以自动定义类型,根据等号后面的变量
C++中,typeid(A).name();可以知道A的类型是什么
3.2 auto简介
使用 auto 定义变量时 必须对其进行初始化 ,在编译阶段编译器需要根据初始化表达式来推导 auto 的实际类 型 。因此 auto 并非是一种 “ 类型 ” 的声明,而是一个类型声明时的 “占位符” ,编译器在编译期会将 auto 替换为 变量实际的类型 。
3.3 auto的使用细则
(1)auto与指针和引用结合起来使用
(auto*定义的必须是指针类型)
auto意义之一:类型很长时,懒得写,可以让他自动推导。
3.4 auto不能推导的场景
// 此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导
void TestAuto(auto a)
{}
void TestAuto()
{
int a[] = {1,2,3};
auto b[] = {4,5,6};
}
3. 为了避免与C++98中的auto发生混淆,C++11只保留了auto作为类型指示符的用法
4. 基于范围的for循环(C++11)
4.1 范围for的语法
void TestFor()
{
int array[] = { 1, 2, 3, 4, 5 };
//加&的原因是e是array内容的拷贝,所以改变e不是改变array里面的内容
for (auto& e : array)
{
e *= 2;
}
//范围for,依次自动取arrar中的数据,赋值给e,自动判断结束
for (auto e : array)//这里写int也可以
{
cout << e << " ";
}
}
4.2 范围for的使用条件
void TestFor(int array[])
{
for(auto& e : array)
cout<< e <<endl;
}
这里的array是数组的首元素的地址,所以范围不定
5. 指针空值nullptr(C++11)
5.1 C++98中的指针空值
//指针初始化
int* p1 = NULL;
int* p2 = 0;
int* p3 = nullptr;//建议用这一种