首页 > 代码库 > 操作符重载之解引用与箭头操作符

操作符重载之解引用与箭头操作符

箭头操作符与众不同。它可能表现得像二元操作符一样:接受一个对象和一个成员名,对对象解引用以获取成员。不管外表如何,箭头操作符不接受显式形参。这里没有第二个形参,因为 -> 的右操作数不是表达式,相反,是对应着类成员的一个标识符。没有明显可行的途径将一个标识符作为形参传递给函数,相反,由编译器处理获取成员的工作。

理解1
当这样编写时:point->action();由于优先级规则,它实际等价于编写:(point->action)();换句话说,我们想要调用的是对 point->action 求值的结果。编译器这样对该代码进行求值:
1.如果 point 是一个指针,指向具有名为 action 的成员的类对象,则编译器将代码编译为调用该对象的 action 成员。
2.否则,如果 point是定义了 operator-> 操作符的类的一个对象,则 point->action 与 point.operator->()->action相同,即执行point的operator->(),然后使用该结果重复这三步。
3.否则,代码出错。

理解2
箭头操作符,一般都有左右两部分:a->b;如何开始:由a开始,分下面两种情况:
1、a是指针,那么就是我们熟悉的,指向我们a类型的成员数据或函数“b”;到这里,就结束了!
2、a是对象,那么a必须有成员函数"operator->"(否则会报错)。那么就调用a的operator->函数。由于operator->返回的可能是指针,也可能是对象,那么就又进入了下一轮递归:是指针还是对象,走1或2,如此递归。 如何结束:不管a是指针或者对象,最终都是走到指针结束。

理解3
重载箭头操作符必须返回指向类类型的指针,或者返回定义了自己的箭头操作符的类类型对象。
如果返回类型是指针,则内置箭头操作符可用于该指针,编译器对该指针解引用并从结果对象获取指定成员。如果被指向的类型没有定义那个成员,则编译器产生一个错误。
如果返回类型是类类型的其他对象(或是这种对象的引用),则将递归应用该操作符。编译器检查返回对象所属类型是否具有成员箭头,如果有,就应用那个操作符;否则,编译器产生一个错误。这个过程继续下去,直到返回一个指向带有指定成员的的对象的指针,或者返回某些其他值,在后一种情况下,代码出错。

示例
#include <iostream>

using namespace std;

class D
{
     public:
          void info(){cout << "I am D" << endl;}
};

class C
{
     public:
          void info(){cout << "I am C" << endl;}
          D* operator*(){return &m_d;}
          D* operator->(){return &m_d;}

     private:
          D m_d;
};

class B
{
     public:
          void info(){cout << "I am B" << endl;}
          C& operator*(){return m_c;}
          C& operator->(){return m_c;}
     private:
          C m_c;
};

class A
{
     public:
          void info(){cout << "I am A" << endl;}
          C* operator*(){return &m_c;}
          C* operator->(){return &m_c;}
     private:
          C m_c;
};

int main()
{
     A a;
     a->info();
     a->operator->()->info();

     B b;
     b->info();
    
     B *bp = &b;
     bp->info();
     A *ap = &a;
     ap->info();
    
     cout << endl << "deference test" << endl << endl;    
     A da;
     (*da)->info();
     B db;
     (*db)->info();
     (*db).info();
     (**db)->info();
     C dc;
     (*dc)->info();

     return 0;
}

运行结果
I am C
I am D
I am D
I am B
I am A

deference test

I am C
I am D
I am C
I am D
I am D

结果分析
箭头操作符和解引用操作符只有作用在类对象上,才会起重载作用;作用在指针上,按照原始的原生态指针的含义处理。

1、a->info();
首先,a是对象,而且有operator->,调用之,返回C*,是个指针。那么,接下来就是调用函数info,找到对应的C的成员函数。
2、a->operator->()->Print();
a调用“->”返回的C*,是个指针;那么接下来就是调用C的成员函数。后面是个operator->(),正好是C的一个成员函数。
C的operator->返回的是D*,是个指针,那么再调用对应的D的成员函数info();
即“a->operator->()->info()”中的operator->()需要看成一个C的成员函数(正好返回D*,可以调用箭头“->”)就好理解了。
3、b->info();
b是对象,调用operator->返回C对象,那么需要再次递归;C是对象,调用operator->返回D*,那么就是指针,可以走向结束:调用对应的函数info。
4、bp->info();
bp是指针,接下来就是调用函数info,找到对应的B的成员函数
5、ap->info();
ap是指针,接下来就是调用函数info,找到对应的A的成员函数

解引用分析:
6、 (*da)->info();da是A类型的对象,(*da)调用operator*返回指向C类型的指针,后同箭头操作符操作规则;
7、(*db)->info()和(*db).info();db是B的对象(*db)返回指向C类型的对象的引用,即(*db)是一个类型为C的对象,->后同箭头操作符操作规则;.操作符直接调用该对象的成员。
8、(*dc)->info();dc是C类型的对象,(*dc)调用operator*返回指向D类型的指针,后同箭头操作符操作规则;

上面的例子仅仅是为了便于理解解引用操作符和箭头操作符,并没有实用价值。在STL源码中,operator*返回对象的引用,operator->返回对象的指针;此处的对象即对应容器中的元素。

操作符重载之解引用与箭头操作符