首先看一个例子
#include <iostream>
using namespace std;
class A
{
void fun()
{
cout << "amazing" << endl;
}
};
int main()
{
A a;
a->fun();//错误。“A”类型没有重载成员“operator ->”
system("pause");
return 0;
}
一般来说,类对象不能使用箭头运算符访问成员函数,而应该使用点运算符。但是,如果该类重载了箭头运算符,则情况发生改变。
看下面这个例子
#include <iostream>
using namespace std;
class A {
public:
A() { i = 1; }
void fun() {
cout << "fun in class A!" << endl;
}
public:
int i;
};
class B {
A a;
public:
B() { i = 2; }
A* operator->() { //重载箭头运算符的函数居然返回的是指针,也就是对象的地址
return &a;
}
void fun() {
cout << "fun in class B!" << endl;
}
private:
int i;
};
class C {
B b;
public:
C() { i = 3; }
B operator->() { //重载箭头运算符的函数返回的是对象
return b;
}
void fun() {
cout << "fun in class C!" << endl;
}
private:
int i;
};
int main()
{
C* p = new C(); //p是类C的一个指针
p->fun();
C c; //c是类C的对象
c->fun();
int i = c->i;
cout << i << endl;
system("pause");
return 0;
}
看见了吗?c是类C的一个对象,却可以使用箭头运算符。更神奇的是,你看一下运算结果,c->fun()居然结果是“fun in class A”,c->i的结果居然是1。
在C++ Primer中关于重载箭头运算符有这样一段总结:
(说明一)
对于形如point->mem的表达式来说,point必须是指向类对象的指针或者是重载了operator->的类的对象。根据point类型的不同,point->mem分别等价于
<1> (*point).mem //point是一个指针
<2>point.operator()->mem //point是类的一个对象
而且,更为重要的是,按照我们一般的理解,重载箭头运算符返回的不应该是一个指针。那么你再看一下下面这段话:
(说明二)
重载箭头操作符必须返回指向类类型的指针,或者返回定义了自己的箭头操作符的类类型对象。
<1>如果返回类型是指针,则内置箭头操作符可用于该指针,编译器对该指针解引用并从结果对象获取指定成员。如果被指向的类型没有定义那个成员,则编译器产生一个错误。
<2>如果返回类型是类类型的其他对象(或是这种对象的引用),则将递归应用该操作符。编译器检查返回对象所属类型是否具有成员箭头,如果有,就应用那个操作符;否则,编译器产生一个错误。这个过程继续下去,直到返回一个指向带有指定成员的的对象的指针,或者返回某些其他值,在后一种情况下,代码出错。
那么现在给你解释一下程序的运行结果(非常重要)<1>p是指针,按照上述说明一,应该调用的是(*p).fun(),这样结果就是“fun in class C!”
<2>c是定义了operator的类的一个对象,按照上述说明一,应该调用c.operator()->fun,接着类C的重载箭头运算符的成员函数返回值类型是类类型的其它对象,那么按照说明二,应该递归应用该操作符,那么现在来到了类B的重载箭头运算符的成员函数,此成员函数的返回值类型为指针,所以按照说明二,编译器对该指针解引用并从结果对象获取指定成员函数fun(),由于在类B的重载箭头运算符函数中返回的是类A的指针,所以调用类A的成员函数fun(),结果就是“fun in class A!”
至于c->i,则是同理,自行感受。