1. 引用定义前言:更多内容请看总纲《嵌入式C/C++学习路》
引用即别名
int a = 10;
int& b = a; // 这里&不是取地址的意思,是引用的意思。 b是一个int型的引用,引用a
int& c = b;
引用必须初始化
int& b; // error!!!
int& b = a;
const int& b = 10;
- 引用不能为空
- 引用不能更换目标
#include2. 把引用作为参数using namespace std; int main() { int a = 10; int &b = a; cout << b << endl; ++b; // b 是 a 的别名,对b的操作就是对a的操作 cout << a << endl; int d = 20; b = d; // d 的值赋给b也就是赋给a cout << a << endl; return 0; }
引用型参数,函数的形参是实参的别名
——在函数中修改实参值
——避免对象复制的开销
常引用型参数
——接受常量型实参
——防止对实参的意外修改
例子:定义一个函数,交换两个参数a,b的值
#includeusing namespace std; // 定义一个函数,交换a,b的值 void swap1(int a, int b) { int c = a; a = b; b = c; } int main(void) { int a = 100, b = 200; swap1(a, b); cout << a << ' ' << b << endl; }
发现这样并不能实现这个功能,因为函数实现的是形参的交换,对实参没有影响。修改如下:
分别通过指针和引用实现:
#includeusing namespace std; // 定义一个函数,交换a,b的值 void swap1(int a, int b) { int c = a; a = b; b = c; } void swap2(int *a, int *b) // 通过指针实现 { int c = *a; *a = *b; *b = c; } void swap3(int &a, int &b) // 通过引用实现 { int c = a; a = b; b = c; } int main(void) { int a = 100, b = 200; // swap2(&a, &b); swap3(a, b); cout << a << ' ' << b << endl; }
再来看一个例子:交换两个字符串(引用指针)
#includeusing namespace std; void swap(const char *x, const char *y) // 此方法不行,交换一级指针的地址应该用二级指针 { const char *z = x; x = y; y = z; } void swap2(const char **x, const char **y) // 懵逼了已经 二级指针 { const char *z = *x; *x = *y; *y = z; } void swap3(const char *&x, const char *&y) { const char *z = x; x = y; y = z; } int main() { const char *x = "Hello world!"; const char *y = "Hello C++"; // swap(x, y); // swap2(&x, &y); swap3(x, y); cout << x << ' ' << y << endl; return 0; }
引用还可以用在结构里:
#includeusing namespace std; struct Student { char name[256]; int age; }; void print(const Student &s) { // const 表示引用目标具有常属性,即不可被修改 cout << s.name << ' ' << s.age << endl; // 每执行一次下面这条语句,age就会加一岁,但是按道理来讲,我们只是为了查看年龄,不应该修改,为了避免这种错误,一般在引用前面加一个const来避免对实参的修改 // cout << s.name << ' ' << ++s.age << endl; } int main(void) { Student s = {"小明", 26}; print(s); }
需要注意的是,实参是常量时,定义函数的形参引用需要加const:
#include3.引用型返回值using namespace std; struct Student { char name[256]; int age; }; void print(const Student &s) { // const 表示引用目标具有常属性,即不可被修改 cout << s.name << ' ' << s.age << endl; // 每执行一次下面这条语句,age就会加一岁,但是按道理来讲,我们只是为了查看年龄,不应该修改,为了避免这种错误,一般在引用前面加一个const来避免对实参的修改 // cout << s.name << ' ' << ++s.age << endl; } void show(const int& i){ // 前面必须加const cout << i << endl; } int main(void) { Student s = {"小明", 26}; print(s); show(100); // 100 是常量,形参也需要是const }
在C++11中可以取地址的、有名字的就是左值,反之,不能取地址的、没有名字的就是右值
引用型返回值,从函数中返回引用,一定要保证在函数返回以后,该引用的目标依然有效
——返回左值
——可以返回全局、静态乃至成员变量的引用
——可以返回在堆中动态创建的对象的引用
——可以返回调用对象自身的引用
——可以返回引用型参数本身
——不能返回局部变量的引用
常引用型返回值
——返回右值
关于左值右值请参考《C++11 左值、右值、右值引用详解》
来看一个例子(返回左值):
#includeusing namespace std; int x; int &foo(void) // 返回可修改的左值 { return x; } int main(void) { foo() = 10; // foo()就是x的别名 cout << x << endl; ++foo(); cout << x << endl; foo() += 10; cout << x << endl; return 0; }
返回成员变量的引用:
#includeusing namespace std; struct Student{ char name[256]; int age; int& howold(void){ return age; } }; int main(void) { Student s = {"张三",22}; int x = s.howold(); cout << x << endl; return 0; }
返回引用型参数本身:
#includeusing namespace std; int& fun(int& r){ // 如果形参写成 int r 就错了,返回的是局部变量的引用 return r; } int main(void) { int y = 100; int& r = fun(y); ++r; cout << y << endl; return 0; }
常引用型返回值,返回右值
#include4. 引用的本质using namespace std; int x = 10; const int &foo(void) { return x; // 带有常属性,只能做右值 } int main(void) { cout << foo() << endl; // foo()++; // 错误 return 0; }
在实现层面,引用就是指针,但在语言层面,引用不是实体类型,因此与指针存在明显的差别:
- 指针可以不初始化,其目标可在初始化后随意变更(除非是指针常量),而引用必须初始化,且一旦初始化就无法变更目标
- 存在空指针,不存在空引用
- 存在指向指针的指针,不存在引用引用的引用
- 存在引用指针的引用,不存在指向引用的指针
- 存在指针数组,不存在引用数组,但存在数组引用
定义:显式转换也叫强制转换,是自己主动让这个类型转换成别的类型
- C风格的显式类型转换-(目标类型)源类型变量
- C++风格的显式类型转换-目标类型(源类型变量)
- 静态类型转换
- ——static_cat<目标类型>(源类型变量)
- ——隐式类型转换的逆变换
- ——自定义类型转换
#includeusing namespace std; int main(void) { int *pi; void *pv = pi; // 任何类型的指针都可以被隐式的转换为void型 // pi = pv; // 错误,转换无效,即void*不可以隐式的转换为任何类型的指针,此时可以用static_cat pi = static_cast (pv); // 静态转换 // double d = static_cast (pv); // 错误,double不能被隐式的转换为void*,所以void*也不能静态转换为double类型 return 0; }
- 动态类型转换
- ——dynamic_cast<目标类型>(源类型变量)
- ——多态父子类指针或引用之间的转换
后面继承和多态再作介绍
- 常类型转换
- ——const_cast<目标类型>(源类型变量)
- ——去除指针或引用上的const属性
#includeusing namespace std; int main(void) { const int *p1; // p1有常属性,p2没有 // int * p2 = p1; // 错误:从类型const int*到类型int*的转换无效 int* p2 = const_cast (p1); // 这样就可以,注意:只能去常属性,要是将int转换为char类型就做不到了 return 0; }
- 重解释类型转换
- ——reinterpret_cast<目标类型>(源类型变量)
- ——任意类型的指针或引用之间的转换
- ——任意类型的指针和整型之间的转换
#includeusing namespace std; struct Student { char name[256]; int age; }; int main(void) { Student s = {"张三",22}; // char* ps = &s; // 将struct看成char类型 , 错误! char *ps = reinterpret_cast (&s); // 重解释 cout << ps << endl; // 既然当作字符串类型,输出看一下结果 : 张三 return 0; }



