在C++中,一个对象可以有多个有效的地址,所以,对于这个时候的比较不是地址问题,而是对象同一性的问题。

代码如下:

#include < iostream >
using namespace std;

class Shape{
    public:
    Shape():a (1){}

    private:
    int a;
};

class Subject{
    public:
    Subject():b (2){}


    private:
    int b;
};

class ObservedBlob: public Shape , public Subject{
    private:
    int c;
    public:
    ObservedBlob ():c( 3){}
};

int main()
{
    ObservedBlob * ob = new ObservedBlob;
    Shape * s = ob;
    Subject * subj = ob;

    cout << ob << endl << s << endl << subj << endl;

    cout << (subj == ob) << endl;

    return 0;
}

以上的代码的运行结果(mingw gcc 4.7.1):
Result

那么问题来了,可以看到subj和ob的地址并不相同,但是对指针进行比较的时候却输出了true,这个是为何?

先看这种继承的内存分布图:
Memory

在前面那种布局里面,s和subj分别指向ob中的对应子对象,它们与ob拥有完全不同的地址。

在后面的布局里面,s对象与subj对象的地址相同,可以看到上图中就是采用这样的布局。

但是无论是哪种布局,ob、s和subj都指向同样的一个ObservedBlob对象,因此编译器必须要确保ob与s和subj的比较结果都是true。所以编译器通过将参与比较的指针值之一调整一定的偏移量来完成这种比较,比如:
ob == subj
可能会被编译器翻译成:ob?(ob + delta == subj) : (subj == 0)

delta是Subject子对象在ObservedBlob对象的偏移量,换句话说,如果subj和ob都是空的话,那么它们相等,否则ob被调整为指向Subject基类子对象后再和subj进行比较。

所以最终可以得到一个很重要的经验,一般而言,当我们处理指向对象的指针或者引用的时候,必须小心避免丢失类型信息。

如果把上面的指针编程void *v = subj然后在判断ob和v的相等情况,就会得到不相等结论。

如果通过复制到void*指针后,就丢失了类型信息了,编译器这是就只能进行原始地址比较,这样的比较对于指向类的指针来说,很少会是正确的。