C++中的虚函数的实现一般是通过虚函数表(C++规范并没有规定具体用哪种方法,但大部分的厂商都选择此方法)。 类的虚函数表是一块连续的内存,每个中记录一个JMP指令的地址。
编译器会为每个有虚函数的类创建一个虚函数表,该虚函数表将被该类的所有对象共享。类的每个虚成员占据虚函数表中的一行。如果类中有N个虚函数,那么其虚函数表将有N*4字节的大小。
虚函数(Virtual Function)是通过一张虚函数表(Virtual
Table)来实现的。简称为V-Table。在这个表中,主要是一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其真实反应实际的函数。这
样,在有虚函数的类的实例中分配了指向这个表的指针的内存,所以,当用父类的指针来操作一个子类的时候,这张虚函数表就显得尤为重要了,它就像一个地图一
样,指明了实际所应该调用的函数。
编译器应该是保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证取到虚函数表的有最高的性能——如果有多层继承或是多重继承的情况下)。 这意味着可以通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。
以上是对虚函数表的描述。要注意编译器是为每个有虚函数的类生成一个虚函数表,当创建对象时候,便在对象内存开始处插入了对应的类的虚函数表地址(可参考汇编来看看)。
无论该虚函数是public、private、还是protected(我们一般习惯于用public,但private和protected也会视范围而用)。在此重点要理解编译器这种产生虚函数表的方式。另外理解这个还要从对象内存地址、函数地址的实际值来思考。
看下面的测试
- #include <stdio.h>
- #include <stdlib.h>
- class A
- {
- //private:a
- protected:
- virtual void f() {printf("base class ");}
- public:
- //virtual void f() {printf("base class");}
- void test(){f();}
- };
- //class B : private A
- class B : public A
- {
- //public:
- //private:
- protected:
- virtual void f() { printf("sub class\n");}
- };
- int main(int argc, char *argv[], char *env[])
- {
- A* p = new B();
- // p->f();
- p->test();
- }
上面这个形式就是:
在基类里面的成员函数内调用子类的虚函数。是框架编程的一种很好方式。(基类里面调用成员函数,默认是通过this指针,所以通过虚函数表就找到真实函数入口地址了)
另注:这里是指的普通成员函数,构造函数和析构函数内部调用虚函数,编译器规定都是调用本类的对应虚函数,否则会次序混乱而产生错误(分析:必须构造,才有对象,然后才能建立虚函数机制(对象实体的虚函数指针指向虚函数表);次序上析构基类时候,继承类已经释放,此时若调用子类虚函数,肯定错误)。
在c++里 构造和析构往往是有特殊身份的
阅读(3612) | 评论(2) | 转发(0) |