十 、C++引用(Reference)
1 定义
1)引用即别名,引用就是某个变量别名,对引用操作和对变量本身完全相同.
2)语法
类型 & 引用名 = 变量名;
注:引用必须在定义同时初始化,而且在初始化以后所绑定的目标变量不能再做修改.
注:引用类型和绑定目标变量类型要一致。
2 常引用
1)定义引用时可以加const修饰,即为常引用,不能通过常引用修改目标变量.
const 类型 & 引用名 = 变量名;
类型 const & 引用名 = 变量名;//和上面等价
2)普通引用也可以称为左值引用,只能引用左值;而常引用也可以称为万能引用,既可以引用左值也可以引用右值。
3)关于左值和右值
左值(lvalue):可以放在赋值表达式左侧,可以被修改
右值(rvalue):只能放在赋值表达式右侧,不能被修改
练习:测试下面表达式结果,是左值还是右值?
int a,b;
a+b;//右值
a+=b;//左值
++a;//左值
a++;//右值
3 引用型函数参数
1)可以将引用用于函数的参数,这时形参就是实参别名,可以通过形参直接修改实参的值;同时还能避免函数调用时传参的开销,提高代码执行效率。
2)引用型参数有可能意外修改实参的值,如果不希望修改实参,可以将形参声明为常引用,提高效率的同时还可以接收常量型的实参。
4 引用型函数返回值
1)可以将函数的返回类型声明为引用,这时函数的返回结果就是return后面数据的别名,可以避免返回值带来的开销,提高代码执行效率.
2)如果函数返回类型是左值引用,那么函数调用表达式结果就也将是一个左值。
注:不要从函数中返回局部变量的引用,因为所引用的内存会在函数返回以后被释放,使用非常危险!可以在函数中返回成员变量、静态变量、全局变量的引用。
int& func(void){
...
return num;
}
func() = 100;//ok
5 引用和指针
1)如果从C语言角度看待引用的本质,可以认为引用就是通过指针实现的,但是在C++开发中,推荐使用引用,而不推荐使用指针.
int i = 100;
int* const pi = &i;
int& ri = i;
*
pi <=等价=> ri
2)指针可以不做初始化,其目标可以在初始化以后随意改变(指针常量除外),而引用必须做初始化,而且一旦初始化所引用的目标不能再改变.
int a=10,b=20;
int* p;//ok
p = &a;
p = &b;
--------------------
int& r;//error
int& r = a;
r = b;//ok,但不是修改引用目标,仅是赋值运算
//下面内容了解
3)可以定义指针的指针(二级指针),但是不能定义引用的指针.
int a = 10;
int* p = &a;
int** pp = &p;//二级指针
------------------------
int& r = a;
int&* pr = &r;//error,引用的指针
int* pr = &r;//ok,仅是普通指针
4)可以指针的引用(指针变量别名),但是不能定义引用的引用。
int a = 100;
int* p = &a;
int* & rp = p;//ok,指针的引用
-----------------------------
int& r = a;
int& & rr = r;//error,引用用的引用
int& rr = r;//ok,但是仅是一个普通引用
5)可以指针数组,但是不能定义引用数组
int i=10,j=20,k=30;
int* parr[3] = {&i,&j,&k};//ok,指针数组
int& rarr[3] = {i,j,k};//error
6)可以定义数组引用(数组别名)
int i=10,j=20,k=30;
int arr[3] = {i,j,k};
int (&rarr)[3] = arr;//ok, 数组引用
7)和函数指针类似,也可以定义函数引用(函数别名)
void func(int i){}
int main(void){
void (*pf)(int) = func;//函数指针
void (&rf)(int) = func;//函数引用
pf(100);
rf(100);
}
十一、 类型转换
1 隐式类型转换
char c = ‘A’;
int i = c;//隐式
-----------------
void func(int i){}
func©;//隐式
-----------------
int func(void){
char c=‘A’;
return c;//隐式
}
2 显示类型转换
2.1 C++兼容C中强制类型转换
char c = ‘A’;
int i = (int)c;//C风格
int i = int©;//C++风格
2.2 C++扩展了四种操作符形式显式转换
1)静态类型转换:static_cast
语法:
目标变量 = static_cast<目标类型>(源类型变量);
适用场景:
主要用于将void*转化为其它类型的指针
举例:static_cast.cpp
#include <iostream>
using namespace std;
int main(void){
int* pi = NULL;
//char c = (long)pi;//C风格强转
char c = long(pi);//C++风格强转
//静态类型转换
void* pv = pi;
pi = static_cast<int*>(pv);//合理
return 0;
}
2)动态类型转换:dynamic_cast//后面讲
语法:
目标变量 = dynamic_cast<目标类型>(源类型变量);
3)去常类型转换:const_cast
语法:
目标变量 = const_cast<目标类型>(源类型变量);
适用场景:
主要用于去掉指针或引用const属性.
举例:const_cast.cpp
#include <iostream>
using namespace std;
int main(void){
//volatile是标准C语言的关键字
//被volatile修饰的变量表示易变的,告诉编译
//器每次使用该变量时,都要小心从内存中读取,
//而不是取寄存器的副本,防止编译器优化引发
//的错误结果.
volatile const int i = 100;
int* pi = const_cast<int*>(&i);
*pi = 200;
cout << "i=" << i << ",*pi=" << *pi
<< endl;//200,200
cout << "&i=" << (void*)&i << ",pi=" <<
pi << endl;
return 0;
}
4)重解释类型转换:reinterpret_cast
语法:
目标变量=reinterpret_cast<目标类型>(源类型变量);
适用场景:
在指针和整型数进行显式转换.
任意类型指针或引用之间显式转换.
eg:已知物理内存地址0x12345678,向该地址存放一个整型数100?
int* paddr = reinterpret_cast<int*>(0x12345678);
*paddr = 100;
代码:reinterpret_cast.cpp
#include <iostream>
using namespace std;
int main(void){
//"\000"-->'\0'
char buf[] = "0001\00012345678\000123456";
struct HTTP{
char type[5];
char id[9];
char passwd[7];
};
HTTP* pt = reinterpret_cast<HTTP*>(buf);
cout << pt->type << endl;//0001
cout << pt->id << endl;//12345678
cout << pt->passwd << endl;//123456
return 0;
}
小结:
1 慎用宏,可以使用const、enum、inline替换
#define PAI 3.14
–》const double PAI = 3.14;
#define SLEEP 0
#define RUN 1
#define STOP 2
–》enum STATE{SLEEP,RUN,STOP};
#define Max(a,b) ((a)>(b)?(a):(b))
–》inline int Max(int a,int b){
return a > b ? a : b;
}
2 变量随用随声明同时初始化
3 尽量使用new/delete替换malloc/free
4 少用void*,指针计算,