引用的应用
引用作为实参
- 引用型形参函数的形参是实参的别名,避免了对象复制的开销
- 非常引用型的参数,在函数内部可以修改参数的值
- 常引用型的参数,可以防止函数内对实参的意外修改
// 当定义一个函数时,只要可以确定在函数内部绝对不修改实参,就可以给引用型形参加上const
#include <iostream>
using namespace std;void Swap(int& x, int& y){ // 非常引用型形参:可以在函数内部修改实参int z = x;x = y;y = z;
}
//指针传入
void Swap(int* x, int* y){int z = *x;*x = *y;*y = z;
}void Print(const int& x, const int& y){ // 常引用型形参:防止函数内部意外修改实参
// x = 666; y = 888;cout << x << ", " << y << endl;
}int main( void ){int a = 10, b = 20;Swap(a,b);
// Swap(&a,&b);cout << "a = " << a << ", b = " << b << endl;Print(a,b);Print(666,888);return 0;
}
引用作为返回值
引用型的返回值,从函数中返回引用一定要保证在函数返回以后,该引用的目标依然有效
- 可以返回全局、静态变量的引用
- 可以返回在堆中动态创建的对象的引用
- 可以返回引用型参数本身
- 可以返回成员变量的引用
- 可以返回调用对象自身的引用
- 不能返回局部变量的引用
引用型的返回值也分为常引用型和非常引用型。
引用和指针的区别
- 指针可以不初始化,其目标可在初始化后随意变更(除非是指针常量),而引用必须初始化,且一旦初始化就无法变更其目标
- 存在空指针,不存在空引用
- 存在指向指针的指针,不存在引用的引用
- 存在指针的引用,不存在引用的指针
- 存在指针数组,不存在引用数组但存在数组引用
// 引用和指针在C++层面的差别
#include <iostream>
using namespace std;int main( void ){int a = 10, b = 20;int* pa = &a; // 指针可以不做初始化,也可以做初始化pa = &b; // 指针在初始化后,可以随意变更它所指向的内存空间int& ra = a; // 引用必须初始化ra = b; // 引用在初始化后,不可以变更它所引用的内存空间// 这里只是用b的值给ra所引用的内存空间赋值pa = NULL; // 存在空指针
// ra = NULL; // 不存在空引用int** ppa = &pa; // 存在二级指针
// int&& rra = ra; // 不存在二级引用int*& rpa = pa; // 存在指针的引用
// int&* pra = &ra; // 不存在引用的指针int* pra = &ra; // ok 但这不是引用的指针int x, y, z;int* p[3] = {&x, &y, &z}; // 存在指针数组
// int& r[3] = {x, y, z}; // 不存在引用数组int arr[3];int(&parr)[3] = arr; // 存在数组引用return 0;
}
指针数组和数组指针
指针数组:元素都是指针的数组
数组指针:指向一个数组的指针,保存的是首个元素的地址
显示类型转换
隐式类型转换
- 任何基本类型变量之间都可以隐式转换
- 任何其它类型的指针到void*都可以隐式转换
- 任何类型的非常指针到同类型的常指针都可以隐式转换
必须显示类型转换的情况
- void*到任何其它类型的指针都必须强转(显式转换)
- 任何类型的常指针到同类型的非常指针都必须强转
- 除了void*之外,其他类型的指针之间都必须强转
静态类型转换
- static_cast<目标类型>(源类型变量)
- 所有隐式类型转换(常类型除外,有专门的常类型转换)的逆转换,都需要静态类型转换
- 自定义类型的转换
动态类型转换
- dynamic_cast<目标类型>(源类型变量)
- 多态父子类指针或引用之间的转换
常类型转换
- const_cast<目标类型>(源类型变量)
- 去除指针或引用上的const属性
重解释类型转换
- reinterpret_cast<目标类型>(源类型变量)
- 任意类型的指针之间的转换或引用之间的转换
- 任意类型的指针和整型之间的转换
- 尽量不要使用该转换,除非以上三种类型都无法实现
// 显式(强制)类型转换
#include <iostream>
using namespace std;int main(void){int a; double b; float c; short d; char e;// 任何基本类型变量之间都可以隐式转换a = b = c = d = e;e = d = c = b = a;// 任何其它类型的指针到void*都可以隐式转换void* pv = &a;pv = &b;pv = &c;pv = &d;pv = &e;// void*到任何其它类型的指针都必须强转(显式转换) ******************************int* pa = static_cast<int*>(pv); // void*-->int*的反向int*-->void*可以隐式转换double*pb = static_cast<double*>(pv); // void*-->double*的方向double*-->void*可以隐式转换float* pc = static_cast<float*>(pv);short* pd = static_cast<short*>(pv);char* pe = static_cast<char*>(pv);// 任何类型的非常指针到同类型的常指针都可以隐式转换const int* cpa = pa;const double* cpb = pb;const float* cpc = pc;const short* cpd = pd;const char* cpe = pe;// 任何类型的常指针到同类型的非常指针都必须强转 ********************************pa = const_cast<int*>(cpa); // const int*-->int*的反向int*-->const int*可以隐式转换pb = const_cast<double*>(cpb);pc = const_cast<float*>(cpc);pd = const_cast<short*>(cpd);pe = const_cast<char*>(cpe);// 除了void*之外,其他类型的指针之间都必须强转pa = reinterpret_cast<int*>(pb); // double* --> int*pa = reinterpret_cast<int*>(38);return 0;
}
面向对象
面向对象的三大要件:封装、继承、多态
如何面向对象
- 至少掌握一种面向对象的语言,如C++
- 深入理解封装、继承、多态等面向对象的重要概念
- 学习设计模式,通过经验积累总结