C++中虚函数表的介绍
来源:互联网 发布:淘宝网店话费充值 编辑:程序博客网 时间:2024/05/08 19:24
一. 多态
多态是指使用相同的函数名来访问函数不同的实现方法,可以简单地概括为“一种接口,多种方法”。C++中,支持编译时多态(静态多态,如运算符重载、函数重载)和运行时多态(动态多态,如派生类、虚函数的实现)。
静态多态与动态多态的实质区别是函数地址是早绑定还是晚绑定。如果函数的调用,在编译器编译期间就可以确定函数的调用地址,并产生代码,则是编译时多态,或者说地址是早绑定的。而如果函数调用的地址不能在编译器期间确定,需要在运行时才确定,则属于运行时多态,也就是晚绑定。
C++动态多态性是通过虚函数来实现的,虚函数允许子类(派生类)重新定义父类(基类)的成员函数,而子类重新定义父类虚函数的做法称为覆盖(override),或称为重写。
最常见的用法是,父类(基类)指针,该指针可以指向任何子类(派生类)对象(实例),然后通过基类的指针调用实际派生类的成员函数,示例如下:
#include <iostream>using namespace std;class Base{public: void f(int x){cout << "Base::f(int) " << x << endl;} void f(float x){cout << "Base::f(float) " << x << endl;} // 须有关键字virtual,此为虚函数 virtual void g(void){cout << "Base::g(void)" << endl;}}class Derive::public Base{public: // virtual关键字,可有可无,此为虚函数 virtual void g(void){cout << "Derive::g(void)" << endl;}}int main(void){ Derive d; Base *pb = &d; pb->f(42); // 运行结果:Base::f(int) 42 pb->f(3.14f); // 运行结果:Base::f(float) 3.14 pb->g(); // 运行结果:Derive::g(void) return 0;}
二. 虚函数表
C++中的虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)实现的,每个含有虚函数的类都有一张虚函数表,表中每一项是一个虚函数的地址,也就是说,虚函数表的每一项是一个虚函数的指针。
#include <iostream>using namespace std;class Base {public: virtual void f() { cout << "Base::f" << endl; } virtual void g() { cout << "Base::g" << endl; } virtual void h() { cout << "Base::h" << endl; }};int main(void) { Base b; b.f(); //"Base::f" b.g(); //"Base::g" b.h(); //"Base::h" return 0;}
Base的实例(即对象b)的虚函数表如下:
注意:在上面这个图中,虚函数表的最后多加了一个结点,这是虚函数表的结束结点,就像字符串的结束符“\0”一样,其标志了虚函数表的结束。这个结束标志的值在不同的编译器下是不同的。
三. 无虚函数覆盖的虚函数表
下面,再让我们来看看继承时的虚函数表是什么样的。
假设有如下所示的一个继承关系:
#include <iostream> using namespace std; class Base { public: virtual void f() { cout << "Base::f" << endl; } virtual void g() { cout << "Base::g" << endl; } virtual void h() { cout << "Base::h" << endl; } }; class Derive:public Base { public: virtual void f1() { cout << "Derive::f1" << endl; } virtual void g1() { cout << "Derive::g1" << endl; } virtual void h1() { cout << "Derive::h1" << endl; } }; int main(void) { Derive d; //派生类对象 return 0; }
在这个继承关系中,子类没有重载任何父类的函数。那么,在派生类的实例(Derive d)中,其虚函数表如下所示:
无虚函数覆盖的虚函数表特点如下:
- 虚函数按照其声明顺序放于表中;
- 父类(派生类)的虚函数在子类(基类)的虚函数前面。
四. 有虚函数覆盖的虚函数表
没有覆盖父类的虚函数是毫无意义的。之所以要讲述没有覆盖的情况,主要目的是为了给一个对比。在比较之下,我们可以更加清楚地知道其内部的具体实现。
下面,我们来看一下,如果子类中有重载了父类的虚函数,会是一个什么样子?
#include <iostream> using namespace std; class Base { public: virtual void f() { cout << "Base::f" << endl; } virtual void g() { cout << "Base::g" << endl; } virtual void h() { cout << "Base::h" << endl; } }; class Derive:public Base { public: //重写父类的f()虚函数 virtual void f() { cout << "Derive::f" << endl; } virtual void g1() { cout << "Derive::g1" << endl; } virtual void h1() { cout << "Derive::h1" << endl; } };int main(void) { Base *p = NULL; // 父类指针 Base b; // 父类对象 Derive d; // 子类对象 p = &b; // 父类指针指向父类对象 p->f(); // 运行结果:"Base::f" p = &d; // 父类指针指向子类对象 p->f(); // 运行结果:"Derive::f" return 0; }
为了让大家看到被继承过后的效果,在这个类的设计中,只覆盖了父类的一个虚函数:f()。那么,对于派生类的实例,其虚函数表会是下面的一个样子:
有虚函数覆盖的虚函数表特点如下:
- 覆盖的f()函数被放到了虚表中原来父类虚函数的位置;
- 没有被覆盖的函数依旧。
本例中:
Base *p = NULL; // 父类指针p = &d; // 父类指针指向子类对象p->f(); // 运行结果:"Derive::f"
由 p(即 &d)所指的内存中的父类虚函数表的 f() 的位置已经被实际子类Derive::f()函数地址所取代,于是在实际调用发生时,是 Derive::f() 被调用了。这就实现了多态。
转载自:http://blog.csdn.net/haoel/article/details/1948051/
- C/C++中时间函数的介绍
- C/C++中时间函数的介绍
- C/C++中时间函数的介绍
- c语言中函数的简单介绍
- C++中虚函数表的介绍
- (转)C/C++中时间函数的介绍
- C语言中,时间调用处理的相关函数介绍
- C语言中函数指针的详细介绍
- 简单的介绍一下C中函数指针
- C++ 中关于虚函数的介绍
- c语言中时间函数介绍
- c中常用string.h函数介绍
- C中string中一些基本函数的介绍与实现
- [C/C++]C++中虚函数的原理和虚函数表
- [C/C++]C++中虚函数的原理和虚函数表
- 一篇非常好的介绍c、c++中有关时间函数的用法
- C语言中strand() rand() time()函数的简单介绍及获取随机数的方法
- C++复习(1):类的简单介绍及C语言中函数用法的扩充
- 洛谷1119灾后重建(floyd)
- HTML学习4:常用标签之列表标签
- position和anchorPoint
- Windows删除语言栏多余的输入法
- 【好工具分享】时间管理利器——今目标
- C++中虚函数表的介绍
- ios tableview
- 151017下午测试的总结
- hibernate 注解
- opencv2-用迭代器访问图像像素
- linux 内存泄漏查找
- 生成带有目录的Markdown格式文档
- 不高兴的小明
- 自定义SwpieRefreshLayout(进入页面自动刷新,下拉刷新,点击加载更多)