首页 / C++ / C++基础篇--虚函数原理
C++基础篇--虚函数原理
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了C++基础篇--虚函数原理,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含3952字,纯文字阅读大概需要6分钟。
内容图文
![C++基础篇--虚函数原理](/upload/InfoBanner/zyjiaocheng/1066/9c9a1134e4154165b1b919172970a690.jpg)
虚函数算是 C++ 最关键和核心的内容之一,是组件的基础。下面先列出一些相关名词,再围绕它们举例说明虚函数的本质实现原理。
基础概念(英文部分来自 C++ 编程思想)
1) 绑定: Connectinga function call to a function body is called binding. (把函数调用和函数实现关联的过程)
2) 早绑定: Whenbinding is performed before the program is run (by the compiler and linker),it‘ s calledearly binding ( 程序运行前,即编译和链接阶段,完成的绑定即为早绑定)。
3) 迟绑定: latebinding, which means the binding occurs at runtime, based on the type of theobject.When a language implements late binding, there must be some mechanism todetermine the type of the object at runtime and call the appropriate memberfunction. ( 迟绑定发生在运行时,不同类型的对象绑定不同函数。实现迟绑定,必须有某种机制确定对象的具体类型然后调用合适的成员函数)。
4) 虚函数表 (VTable): 一个存储于常量区的函数指针表,类似 函数指针数组。 每个含有虚函数的类(基类及派生类)各自包含一张虚函数表(一个类一张表),表中依次存放虚函数地址 。 派生类vt able 继承 它各个基类的vt able ,这里继承是指:基类vt able 中包含某item,派生类vt able 中也将包含同样item,但值可能不同。如派生类 (override) 重新实现了某虚函数,则它的vt able 中 该项item指向新写的虚函数,若未重新实现则沿用基类vt able 中对应项的值。
5) 指向虚函数表的指针 (vtptr): 所有包含虚函数的类所实例化的对象里,都包含该指针,运行时对象借助于它寻址到虚函数表,从而完成后绑定。因此每个包含虚函数的对象,相比普通对象会额外多占用一个指针型的存储空间。
魔术与揭秘
class Base //基类,包含virtual函数
{
public:
virtual void output(){ cout << "Base::output()" << endl;}
};
// 派生两个类 Drv0 和 Drv1
class Drv0 : public Base
{
public:
void output () { cout << "Drv0:: output ()" << endl;}
};
class Drv1 : public Base
{
public:
void output () { cout << "Drv1:: output ()" << endl;}
};
void main()
{
Base b;
Base* pb = &b;
pb-> output (); //输出Base::output()
Drv0 d1;
pb =reinterpret_cast<Base*>(&d1);
pb-> output (); //输出Drv0:: output()
Drv0 d2;
pb =reinterpret_cast<Base*>(&d2);
pb-> output (); //输出Drv0:: output()
Drv1 d3;
pb = reinterpret_cast<Base*>(&d3);
pb-> output ();
//输出Drv1:: output()
}
经过一些中间封装变换,最终同样的 ”pb-> output ()” 运行时选择了不同函数,得到不同结果。奇妙的魔术?别急,下面用结构体 实现类似功能:
typedef void (*pvfun)( );
const pvfun pf_Base[1]= {Base_output};
const pvfun pf_Drv0[1]= {Drv0_output};
const pvfun pf_Drv1[1]= {Drv1_output};
typedef struct BASE
{
void *vtptr;
int mBase;
}Base;
void Base_output() { printf("Base::output()"); }
typedef struct DRV0
{
void *vtptr;
int mBase;
int mDrv0
}Drv0;
void Drv0_output() { printf("Drv0::output()"); }
typedef struct DRV1
{
void *vtptr;
int mBase;
int mDrv1
}Drv1;
void Drv1_output() { printf("Drv1::output()"); }
void main()
{
Base b;
b.vtptr =pf_Base ①
Base* pb =&b; //
*((pvfun)(pb->vtptr+0))(); //调用Base_output()
Drv0 d1;
d1.vtptr = pf_Drv0 ②
pb = (Base*)(&d1);
*((pvfun)(pb->vtptr+0))(); // 调用 Drv0_output ()
Drv0 d2;
d2.vtptr = pf_Drv0
pb = (Base*)(&d2);
*((pvfun)(pb->vtptr+0))() //调用Drv0_output ()
Drv1 d3;
d3.vtptr = pf_Drv1 ③
pb = (Base*)(&d3);
*((pvfun)(pb->vtptr+0))() //调用Drv1_output ()
}
上例同样实现了用相同形式调用不同函数,但这次能清楚看出猫腻所在 : ①②③处分别为结构体成员vtptr赋了不同值。魔术揭穿了,还记得么:”所有软件问题都可以通过增加一个中间层解决”。表面的神奇是依靠VTable和vtptr组成的中间层在背后耍把戏。
例中pf_Base/pf_Drv0/pf_Drv1就是虚函数表VTable;各结构体的成员vtptr就是指向VTable的指针;①②③处是把vtptr与各自struct对应的VTable关联。只不过这些在C++中都隐藏不可见,由编译器自动生成和处理:
1 ) 虚函数表与类关联:编译器在编译时自动为每个包含虚函数的类及其派生类各自单独生成一张虚函数表,用于 存放虚函数指针。注意:基类 与派生类各有各的虚表,独立存放于不同地址,唯的一关联是: 派生类如果没重新实现某基类虚函数,其 VTable 对应条目 中默认存放基类虚函数地址以做后备。
2 )对象与虚函数表关联:对包含虚函数的类, C++ 编译器为 其每个对象 插入一个指针成员 vtptr ,指向该类的虚函数表,即同类对象的 vtptr 值相同。 v tptr 值 在构造函数中初始化(编译器自动加入) ,即使该类没定义构造函数,默认构造函数也会初始化 v tptr 。
3)上面两步说明对象实例化一完毕,就已经和具体虚函数实现挂钩,调用时看似智能的选择不过是顺藤摸瓜:
Drv0 d1; //这一步背后d1->vtptr= VTable(Drv0),其中VTable[0]=(*Drv0::output)()
pb =reinterpret_cast<Base*>(&d1); //编译器支持指针强制向上类型转换,把派生类对象的地址赋给基类指针,pb值仍是&d1
pb-> output(); //d1->vtptr[0](),即调用Drv0:: output ()
总结虚函数实现原理:
编译期建立 vtable 表,设定表中元素;
执行期间在对象创建时的构造函数中关联vtptr和vtable表;
借助于 指针支持的以小引大, 通过强制转换将派生类对象的地址赋给基类指针;
通过基类指针调用虚函数, 先取得对象中的 vtptr ( obj->vtp tr ),再找到其所 指的对应于特定父类或子类的虚函数表( VTable=*(vtptr) ),然后表头加偏移量寻址到相应函数指针 (vfunptr = VTable[offset]) ,最后 执行 *vfunptr() 。
这就是 C++ 通过虚函数实现多态的背后原理,多态使我们可统一用指向基类对象的指针调用所有基类/派生类的虚函数实现,到底会调哪个,关键看对象的vtptr指针指向了哪个类的VTable,而这点在对象实例化时会通过构造函数隐含设置好。
以一个问题结尾,可否在类的构造函数中调用虚函数,为什么?
原文:http://blog.csdn.net/ipmux/article/details/44856549
内容总结
以上是互联网集市为您收集整理的C++基础篇--虚函数原理全部内容,希望文章能够帮你解决C++基础篇--虚函数原理所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。