再认识虚函数表,及虚函数
来源:互联网 发布:文笔好的网文推荐知乎 编辑:程序博客网 时间:2024/06/08 11:08
再认识虚函数表,及虚函数 2012-08-21 11:49:37
分类: C/C++
C++中的虚函数的实现一般是通过虚函数表(C++规范并没有规定具体用哪种方法,但大部分的编译器厂商都选择此方法)。 类的虚函数表是一块连续的内存,每个内存单元中记录一个JMP指令的地址。
编译器会为每个有虚函数的类创建一个虚函数表,该虚函数表将被该类的所有对象共享。类的每个虚成员占据虚函数表中的一行。如果类中有N个虚函数,那么其虚函数表将有N*4字节的大小。
虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。简称为V-Table。在这个表中,主要是一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其真实反应实际的函数。这 样,在有虚函数的类的实例中分配了指向这个表的指针的内存,所以,当用父类的指针来操作一个子类的时候,这张虚函数表就显得尤为重要了,它就像一个地图一 样,指明了实际所应该调用的函数。 编译器应该是保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证取到虚函数表的有最高的性能——如果有多层继承或是多重继承的情况下)。 这意味着可以通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。
以上是对虚函数表的描述。要注意编译器是为每个有虚函数的类生成一个虚函数表,当创建对象时候,便在对象内存开始处插入了对应的类的虚函数表地址(可参考汇编来看看)。
无论该虚函数是public、private、还是protected(我们一般习惯于用public,但private和protected也会视范围而用)。在此重点要理解编译器这种产生虚函数表的方式。另外理解这个还要从对象内存地址、函数地址的实际值来思考。
看下面的测试
上面这个形式就是:在基类里面的成员函数内调用子类的虚函数。是框架编程的一种很好方式。
(基类里面调用成员函数,默认是通过this指针,所以通过虚函数表就找到真实函数入口地址了)
另注:这里是指的普通成员函数,构造函数和析构函数内部调用虚函数,编译器规定都是调用本类的对应虚函数,否则会次序混乱而产生错误(分析:必须构造,才有对象,然后才能建立虚函数机制(对象实体的虚函数指针指向虚函数表);次序上析构基类时候,继承类已经释放,此时若调用子类虚函数,肯定错误)。
在c++里 构造和析构往往是有特殊身份的
编译器会为每个有虚函数的类创建一个虚函数表,该虚函数表将被该类的所有对象共享。类的每个虚成员占据虚函数表中的一行。如果类中有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++里 构造和析构往往是有特殊身份的
99%的情况,被子类重写的虚拟函数,在子类中都可定义为private,这也是最科学的,代码应当严谨,尊守最小化原则。如果一个虚拟函数是子类对象直接调用,则要考虑合理性了。
- 再认识虚函数表,及虚函数
- [收集]虚函数及虚函数表
- 虚函数及虚函数表
- 虚函数及虚函数表
- 虚函数和纯虚函数及虚函数表
- 引用及虚函数
- 虚函数及继承
- 虚函数及继承
- 虚函数及继承
- 虚函数及继承
- 虚函数及继承
- 简谈多态及虚函数
- 虚函数及继承
- C++学习笔记-虚函数及虚函数表
- c++虚函数表及虚函数指针
- 虚函数及虚函数表解析
- 虚函数及纯虚函数
- 关于C++函数重载及虚函数
- 学习小杂记
- 双目视觉算法研究(二)相机模型和直接线性法(DLT)
- 解决linux进入时提示“用户的$HOME/.dmrc 已被忽略”
- Java内存原型分析:基本知识
- eclipse 中配置spring
- 再认识虚函数表,及虚函数
- 非常简单的Python HTTP服务
- betwixt xml转换成对象(XML节点大写)
- @@IDENTITY与SCOPE_IDENTITY()
- 在PopupWindow中不可以使用SurfaceView
- 2013 编程之美挑战赛 集会
- java中使用公钥加密私钥解密原理实现license控制
- 安装与配置windbg的symbol(符号)
- 图示C#中MessageBox用法大全