在上一篇中,咱们弄清楚指针和引用的基本区别,下面从「高质量c++编程」的角度剖析指针与引用!
一、什么时候用 指针与引用?
- 首先,有几个意识。
- Firstly, 任何情况下引用都不能用指向空值,引用必须总是指向某个对象。
- Secondly, 若用某变量(对象)指向另一个变量(对象),且该变量(对象)具备指向任何对象的能力,这时最好将变量(对象)声明为指针,因为指针可以不指向任何变量(对象)。
//空指针(null pointer)不指向任何对象,所以使用指针前一般先进性检查操作,检验指针是否为空: // 先介绍3中生成空指针的方法 int *p1 = nullptr; // equals to : int *p1 = 0; 【C++11新标准】 int *p2 = 0; // 直接将指针初始化为字符常量 0 int *p3 = NULL; // equals to : int *p3 = 0; // 后面会紧接着介绍 测空
- 相反,如果变量肯定指向一个非空变量(对象),这时就把变量声明为引用。下面举一个“毒代码”:
这是非常有害的,编译器仍然产生输出,不过,鬼知道结果,这个不确定的输出将摧毁你整个代码大厦(编译器能产生一些输出,导致任何事情都有可能发生)。char *pc = nullptr;// 设置指针为空值 char & rc = *pc;// 让引用指向空值
- 另一外测试代码:
#include <iostream> //#include <vector> using namespace std; void fun( int *x ){ x = x + 1; // 注意这段代码,对指针[而非指针变量]进行加1操作,结果将不可预测 } int main() { cout << "请输入1个整数:" << endl; int i1; cin >> i1; fun( &i1 ); cout <<"i1 = " << i1 << endl; system("pause"); return 0; }
- 由局部变量变化可知,编译器仍然产生输出,x 本身表示指针的值(地址值),指针值(地址值)+1后对应新的内存单元(地址值),新的内存单元中对应的变量值为{-858993460} = 2^33 - 32;相当于地址变化,但 i1 的值由于对应着固定的内存单元并不发生变化。
测试代码继续上:
这也意味着前面一篇文章中所提到的引用的空间和时间优势,时间上更快,占用内存更小。string &rs;// 错误,引用必须被初始化 string str("xy32y"); string &rs = str;// 正确,rs指向s 指针没有这样的限制。 string *ps;// 未初始化的指针,合法但危险, 应尽量避免
void printDouble1(double &rd) // 这里提一下,引用没有 const,指针有 const; { cout << rd; // 不需要测试rd,它肯定指向一个double值 } 相反,指针则应该总是被测试,防止其为空: void printDouble2(const double *pd) { if (pd) // if (true) 若非空指针,即指针指向一个内存单元 { // 检查是否为NULL cout << *pd; } }
指针可以被重新赋值以指向另一个不同的对象。引用can only指向在初始化时被指定的对象,不能改变。string s1("Nancy"); string s2("Clancy"); string &rs = s1; // rs 引用 s1 string *ps = &s1; // ps 指向 s1 rs = s2; // rs 仍旧引用s1, 但是rs传递的是实参, 故s1的值被赋值为"Clancy" ps = &s2; // ps 现在指向 s2; 喜新厌旧,换了新对象
小结:一 存在不指向任何对象的可能(在这种情况下,你能够设置指针为空)=>指针- 二 在不同的时刻指向不同的对象(在这种情况下,你能改变指针的指向)=>指针
- 三 总指向一个对象,且之后不改变指向 => 引用
- 四 重载某个操作符 = > 引用
- 重点拆解一下四
- 最普通的例子是操作符[].这个操作符典型的用法是返回一个目标对象,其能被赋值。
但是这样会使得v看上去象是一个向量指针。因此你会选择让操作符返回一个引用。(这有一个有趣的例外,参见条款30)vector<int> v(10); // 建立整形向量(vector),大小为10; // 向量是一个在标准C库中的一个模板(见条款35) v[5] = 10; // 这个被赋值的目标对象(变量)就是操作符[]返回的值 如果操作符[]返回一个指针,那么后一个语句就得这样写: *v[5] = 10;
当必须指向一个对象且不改变其指向时,或者在重载操作符并为防止不必要的语义误解时,应使用引用。而在除此之外的其他情况下,则应使用指针
void fun(int *p, int &r); int a = 1; int b = 1; fun(&a, b);
指针本身的值(地址值)是以passby value进行的,你能改变地址值,但这并不会改变指针所指向的变量的值
p = some other pointer;// 起始地址值随机值, 右移一位后地址值改变一个int型长度4 Byte, 输入的整数位333, i1 依旧为 333【见图一】
但能用指针来改变指针所指向的变量的值,- 倘若红线处 修改为“ *x = *x +1 ”, 则有
*x = 333+1; // 且指针所指变量 i1 将也加1 变为 334【见图二】 - 如下面两个图:
- 图一
- 图二
- 摘自《「高质量c++编程」》,不过下面这些着实是等到使用者达到一定经验才会遇到。
- 尽可能使用引用,实在不得已,使用指针。
- 引用出现的典型场合是对象的表面,而指针用于对象内部。
当你不需要“重新指向”时,引用一般优先于指针被选用。这通常意味着引用用于类的公有接口时更有用,不过博主真的很喜欢这句话,“程序员 应该针对要解决的问题写代码,而不是不经思考的套娃,高效,简洁,质优才是一个程序员应该追求的”