7. 指针与引用

7.1. 不同点

1. 指针是一个对象,有存储的 地址 ,存储的数据类型是数据的地址;非常量指针可以被重新赋值,指向另一个对象。引用是对象的别名,必须初始化并总是指向(代表)最初绑定的那个对象,对对象及其引用进行取地址操作得到的结果相同。

 1#include <iostream>
 2using namespace std;
 3
 4int main(int argc, char ** argv)
 5{
 6    int k = 1;
 7    int* pk = &k;
 8    int& rk = k;
 9
10    cout << "&k:" << &k << endl;   // 0029FC44
11    cout << "k:" << k << endl;     // 1
12
13    cout << "&pk:" << &pk << endl; // 0029FC68
14    cout << "pk:" << pk << endl;   // 0029FC44 (pk = &k)
15    cout << "*pk" << *pk << endl;  // 1
16
17    cout << "&rk:" << &rk << endl; // 0029FC44 (&rk = &k)
18    cout << "rk:" << rk << endl;   // 1
19
20    return 0;
21}

2. 指针可以有多级,但是引用只能是一级(不存在 引用的引用 )。

3. 有 null pointer ,没有 null reference ,故使用前无需检查是非为空。

1void rValue(const int &x)
2{
3    cout << x << endl;
4}
5
6void pValue(const int* p)
7{
8    if(p) cout << *p << endl;
9}

一个例子:

1string s1("nancy");
2string s2("candy");
3string& rs = s1;
4string* ps = &s2;
5rs = s2; // rs仍指向s1,但是s1值变为"candy"。
6ps = &s2; // ps指向s2,s1无变化

7.2. 函数返回对象

考虑一个有理数的类,内含一个函数用来计算两个有理数的乘积。

 1class Rational
 2{
 3friend
 4const Rational operator*(const Rational& lhs, const Rational& rhs);
 5// const Rational& operator*(const Rational& lhs, const Rational& rhs);
 6
 7public:
 8    Rational(int _n = 0, int _d = 0): n(_n), d(_d){}
 9private:
10    int n, d;
11};
12
13inline const Rational operator*(const Rational& lhs, const Rational& rhs)
14{
15    return Rational(lhs.n * rhs.n, lhs.d * rhs.d);
16}
17// 这样做需要承担返回值的构造成本和析构成本
18// 但行为是正确的
  • 绝对不要返回指向一个 local stack 对象的 pointer 或 reference。 局部变量会在函数返回后被销毁,因此被返回的引用就成为了“无所指”的引用,程序会进入未知状态。

    1const Rational& operator*(const Rational& lhs, const Rational& rhs)
    2{
    3    Rational result(lhs.n * rhs.n, lhs.d * rhs.d); // result 是 local 对象
    4    return result;
    5}
    
  • 绝对不要返回指向一个 heap-allocated 对象(new)的 reference。 虽然不存在局部变量的被动销毁问题,但是面临其它局面:被函数返回的引用只是作为一个临时变量出现,而没有被赋给一个实际的变量,那么无法获取该引用背后的指针,则该引用所指向的空间(由new分配)就无法释放,造成 memory leak。

     1const Rational& operator*(const Rational& lhs, const Rational& rhs)
     2{
     3    Rational* result = new Rational(lhs.n * rhs.n, lhs.d * rhs.d); // result 是 heap-allocated 对象
     4    return *result;
     5}
     6
     7int main()
     8{
     9    Rational w, x, y, z;
    10    w = x * y * z; // 这里使用了两次 new
    11    return 0;
    12}
    13// 主函数结束时,已经执行了4 + 2次构造函数,4 次析构函数
    
  • 绝对不要返回指向一个 local static 对象的 pointer 或 reference,因为有可能同时需要多个这样的对象。

7.3. 参考资料

  1. 《Effective C++》条款 21。

  2. C++ 把引用作为返回值

  1. 在函数内new一个对象,如果作为引用返回,是不是就可以不用delete了?