猜猜结果啊,买定离手。

  结果是:CB   CB   CC    CC

  在43行的地方,修改了oB的虚表指针,让其指向CC类的虚表。

  但是oB.ShowID()没理会我们的修改,还是调用CB类的ShowID。反汇编,发现他没走“获取虚表指针,在虚表中得到相应的函数地址”这一套,直接调用了。因为一般人不会闲着蛋疼去改对象的虚表指针的,对象的类型是明确的,编译器可以通过这些信息确定调用的函数地址,所以没必要走他一套,这样效率还更高。

  而pCB->ShowID()不同了,他很乖地地走了流程,因为一个父类指针可以指向一个子类对象,编译器无法找信息,所以走流程。

  那现在纠结了,为神马 ((CB*)(&oB))->ShowID() 输出CB。

  反汇编看,发现编译器又擅自做主,没有走指针的流程。

  那你猜猜((Base*)(&oB))->ShowID();输出的是什么?CC。

  比较二者的差异,可以大概发现一些端倪,什么时候走流程,什么时候不走。

  后是Test(oB)了,前面说过引用的本质是指针,所以这个结果很好理解。

  还有,想过

1: void Test2( Base oP )
2: {
3:     oP.ShowID();
4: }

  拷贝的时候有没有拷贝虚表指针吗?试试知道,厄…发现没有。

  前面说过这样会调用拷贝构造函数,但是你在这个函数你没有写虚表指针的赋值。但是邪恶的编译器已经帮你悄悄加上去了哈哈哈哈~。(唉?节操呢)

  RTTI

  每个类有特定的虚表地址,每个对象会保存这个虚表地址,应该想到了吧,偷懒,不写了。

  综上。可以看到,面向对象机制在底层并不特别,机制的实现主要靠的是编译器。