virtual的理解

来源:互联网 发布:linux vi修改ip地址 编辑:程序博客网 时间:2024/06/06 00:26
#include "stdafx.h"
#include <iostream>
using namespace std;


class A
{
public:
void fA() { cout << "A::fA()" << endl; }
virtual void vfA() { cout << "A::vfA()" << endl; }
void emptyB(){ cout << "A::emptyB()" << endl; }
void vfAonly() { cout << "A::vfAonly()" << endl; }
};


class B : public A
{
public:
void fB() { cout << "B::fB()" << endl; }
virtual void vfA() { cout << "B::vfA()" << endl; }
virtual void vfB() { cout << "B::vfB()" << endl; }
void emptyA() { cout << "B::emptyA()" << endl; }
virtual void vfAonly() { cout << "B::vfAonly()" << endl; }
};


int main()
{
A* p = new B;
/*首先,p是指向A的指针,p指向了B的对象,能这样的话,说明A 是B的基类


p是A* 类型的, 但是p不务正业,指向了派生类 B的对象
(B*)p 就是把p强制转换成B*类型了。
*(B*)p 就是对p解引用, 实际上就是 new B()的对象了
然后 B& r = *(B*)p; 就是定义一个B的引用类型 r ,引用的是B的对象(也就是 new B())
*/
B& r = *(B*)p;


p->fA();            // 1A::fA()
/*行1. 在编译器眼中,p就是一个纯粹的A类指针,跟他指向的B类对象没有任何联系。因此,当看到
p->fA()时,编译器便去A的定义中寻找fA,找到了,于是生成调用代码。*/


//p->fB();            // 2
//行2. 这行如果不被注释,编译器去A的定义中寻找定义fB,但是找不到这个名字,便会输出错误信息。


p->vfA();            // 3B::vfA()
/*行3. 编译器继续去A定义中寻找vfA,这次找到了,而且发现关键字virtual,于是,采用虚拟函数调用
代码生成技术,根据vfA的偏移值,生成代码调用虚拟函数表中该偏移值指向的函数。特别指出的
是,在静态编译期间,编译器只知道偏移值,并不知道运行时该偏移到底指向什么函数。实际效果
是,因为运行时,p指向的是B对象,因此调用的是B的虚拟函数vfA().*/


//p->vfB();            // 4
/*行4. 这行如果不被注释,编译器去A的定义中寻找名字vfB,找不到,出错。记住第一条原则,编译器
是静态编译,不知道p和类B有联系。*/


//p->emptyA();        // 5
//行5. 同4,找不到名字emptyA。




p->emptyB();        // 6行6. 简单,找到名字emptyB. A::emptyB()
p->vfAonly();        // 7行7. 简单,找到名字vfAonly。A::vfAonly()


cout << endl;


r.fA();                // 8A::fA()
/*行8. 从这里开始,函数由B类引用r调用。在编译器眼中,r就是一个纯粹的B类引用,他不假设r和A有任何
关系。因此这一行,编译器去B类定义寻找名字fA。由于B继承自A,包括所有A的public函数定义,
编译器成功找到A::fA。*/


r.fB();                // 9行9. 类似行8,找到B自身的函数定义fB。B::fB()


r.vfA();            // 10B::vfA() 行10. 类似行3,编译器生成代码调用虚拟函数表某偏移指向的函数。运行时该偏移指向B::vfA.
r.vfB();            // 11B::vfB() 行11. 编译器生成代码调用虚拟函数表某偏移指向的函数。运行时该偏移指向B::vfB.
r.emptyA();            // 12B::emptyA() B::emptyA()行12. 简单,找到名字emptyA.
r.emptyB();            // 13A::emptyB() 行13. 简单,找到名字A::emptyB. 因为B继承自A。
r.vfAonly();        // 14B::vfAonly()
/*行14. 编译器生成代码调用虚拟函数表某偏移指向的函数。运行时该偏移指向B::vfAonly. 为什么编译器知道
指向的是B的虚拟函数vfAonly而不是A的非虚拟函数呢?这跟另一个静态编译规则,名字隐藏,有关。
继承类的作用域中如果有基类的同名函数,继承类中的名字将隐藏基类同名函数,因此这时,编译器看
不见A::vfAonly。*/


delete p;
return 0;
}