多态和虚函数.docx
- 文档编号:2134178
- 上传时间:2022-10-27
- 格式:DOCX
- 页数:14
- 大小:24.55KB
多态和虚函数.docx
《多态和虚函数.docx》由会员分享,可在线阅读,更多相关《多态和虚函数.docx(14页珍藏版)》请在冰豆网上搜索。
多态和虚函数
多态和虚函数
声明:
文章里面的很多话都是直接出自于书里面,如果侵权请通知我,我会拿掉。
最近在看C++方面的书籍,所以随便记些东西。
面向对象程序设计语言的3个基本特征:
第一:
数据抽象.第二:
继承。
第三:
多态性。
多态性跟虚函数之间的关系就是:
C++中通过虚函数来实现多态性
多态性:
只要涉及到一个函数的多种行为才能称为多态。
为指明某个成员函数具有多态性,用关键字virtual来标志其为虚函数。
基类中用virtual声明的虚函数,自动的向下带给其子类,所以子类中的virtual可以省略,也可以写上。
多态函数必须:
名字相同,参数相同,返回值相同。
有一种例外:
如果基类中的虚函数返回一个基类的指针或者基类的引用,子类中的虚函数返回一个子类的指针或者引用,则C++认为是同名虚函数,而进行迟后联编。
虚函数的限制:
第一:
只有类的成员函数才能声明为虚函数。
第二:
静态成员函数不能是虚函数。
第三:
内联函数(inline)不能是虚函数,因为内联函数不能在程序运行中动态确定其位置。
第四:
构造函数不能是虚函数。
第五:
析构函数可以是虚函数,而且通常都这么做。
例如:
voidfinishwithobject(Base*pHeadObject)
{
deletepHeadObject;
}
pHeadObject传递过来的是对象指针,它或者指向基类或者指向子类,在进行deletepHeadObject时,要调用析构函数,但是执行基类的析构函数?
还是执行之类的析构函数?
将基类声明为虚函数就可以解决。
向上类型转换(upcasting):
取一个对象的地址(指针或者引用),并将其作为基类的地址来处理。
如下代码:
#include
usingnamespacestd;
enumnote{middleC,Csharp,Eflat};
classInstrument{
public:
voidplay(note)const{
cout<<"Instrument:
:
play"< } }; classWind: publicInstrument{ public: voidplay(note)const{ cout<<"Wind: : play"< } }; voidtune(Instrument&i){ //tune(Instrument*i) i.play(middleC); //i->play(middleC); } intmain(){ Windflute; tune(flute);//Upcasting //tune(&flute); } 注意上面代码的指针和引用的区别。 上面的代码返回Instrument: : play它并没有返回Wind: : play 解决这个问题的方法就是使用虚函数virtual,在函数声明的时候加上virtual关键字,定义的时候不需要.如果一个函数在基类里面被声明为虚函数,那么在所有的派生类里面都是虚函数.在派生类中virtual函数的重定义通常称为重写(overriding). 在一个设计良好的OOP程序中,大多数甚至所有的函数都沿用tune()模型,只与基类接口通讯,这样的程序是可扩展的(extensible)见如下代码 #include usingnamespacestd; enumnote{middleC,Csharp,Cflat};//Etc. classInstrument{ public: //Purevirtualfunctions: virtualvoidplay(note)const=0; virtualchar*what()const=0; //Assumethiswillmodifytheobject: virtualvoidadjust(int)=0; }; //Restofthefileisthesame... classWind: publicInstrument{ public: voidplay(note)const{ cout<<"Wind: : play"< } char*what()const{return"Wind";} voidadjust(int){} }; classPercussion: publicInstrument{ public: voidplay(note)const{ cout<<"Percussion: : play"< } char*what()const{return"Percussion";} voidadjust(int){} }; classStringed: publicInstrument{ public: voidplay(note)const{ cout<<"Stringed: : play"< } char*what()const{return"Stringed";} voidadjust(int){} }; classBrass: publicWind{ public: voidplay(note)const{ cout<<"Brass: : play"< } char*what()const{return"Brass";} }; classWoodwind: publicWind{ public: voidplay(note)const{ cout<<"Woodwind: : play"< } char*what()const{return"Woodwind";} }; //Identicalfunctionfrombefore: voidtune(Instrument&i){ //... i.play(middleC); } //Newfunction: voidf(Instrument&i){i.adjust (1);} intmain(){ Windflute; Percussiondrum; Stringedviolin; Brassflugelhorn; Woodwindrecorder; tune(flute); tune(drum); tune(violin); tune(flugelhorn); tune(recorder); f(flugelhorn); } 注意上面代码tune函数的参数是---"基类的引用",这种形式在OOP编程里面非常常见,我们要记住基类指针可以指向任何从它派生出来的子类. 还有就是在上面的代码里面Brass和Woodwind这两个类没有重写adjust()函数,当出现这种情况的时候,将会自动调用继承层中"最近"的定义. 其实我们完全可以不关系虚函数在C++机制中是怎么实现的,只知道怎么用就可以了.但是如果我们知道些C++是怎么实现这些的也许对我们的编程是有好处的,所以简单说说C++是怎么实现这些的. 为了达到这个目的,编译器对每个包含虚函数的类创建了一个表VTABLE.并且在VTABLE中,编译器在放置特定类的虚函数的地址.然后编译器秘密的在每个带有虚函数的类中放置了一个称为vpointer的指针(VPTR),来指向这个对象的VTABLE.为每个类设置VTABLE,初始化VPTR,这些都是自动发生的,我们不用考虑. 下面我们从类对象的大小来进一步理解上面说的VPTR指针,见如下代码: #include usingnamespacestd; classNoVirtual{ inta; public: voidx()const{} inti()const{return1;} }; classOneVirtual{ inta; public: virtualvoidx()const{} inti()const{return1;} }; classTwoVirtuals{ inta; public: virtualvoidx()const{} virtualinti()const{return1;} }; intmain(){ cout<<"int: "< cout<<"NoVirtual: " < cout<<"void*: "< cout<<"OneVirtual: " < cout<<"TwoVirtuals: " < }///: ~ 在上面这段代码里面,没有虚函数类对象的大小正好是数据成员的大小--int类型的大小,包含有一个或者多个虚函数的类对象编译器向里面插入了一个VPTR指针(void*),指向一个存放函数地址的表就是我们上面说的VTABLE,这些都是编译器为我们做的我完全可以不关心这些.所以有虚函数的类对象的大小是数据成员的大小加上一个VPTR指针(void*)的大小. 总结一下VPTR和VTABLE和类对象的关系: 每一个具有虚函数的类都有一个虚函数表VTABLE,里面按在类中声明的虚函数的顺序存放着虚函数的地址,这个虚函数表VTABLE是这个类的所以对象所共有的,也就是说无论用户声明了多少个类对象,但是这个VTABLE虚函数表只有一个. 在每个具有虚函数的类的对象里面都有一个VPTR虚函数指针,这个指针指向VTABLE的首地址.每个类的对象都有这么一种指针. 抽象基类和纯虚函数: 抽象基类: C++允许程序员声明一个不能有实例对象的类,这个类唯一的用途就是"被继承".一个抽象类至少有一个纯虚函数.当继承一个抽象类的时候,必须实现所有的纯虚函数,否则继承出的类也是一个抽象基类.抽象基类用来确定什么是对于所有派生类是共有的---除此之外别无用途. 纯虚函数: 使用关键字virtual并在后面加上"=0".例如: virtualvoidpaly(note)const=0.这样做是告诉编译器在VTABLE中为函数保留一个位置,但是在这个特定的位置中不放地址,以便子类用自己的实现函数定义来重载.见如下代码: #include usingnamespacestd; enumnote{middleC,Csharp,Cflat};//Etc. classInstrument{ public: //Purevirtualfunctions: virtualvoidplay(note)const=0;
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 函数
![提示](https://static.bdocx.com/images/bang_tan.gif)