C++多态详谈
来源:互联网 发布:sql2000数据库下载 编辑:程序博客网 时间:2024/05/16 15:31
多态
1.静态多态
静态多态(早绑定):编译器在编译期间完成的,编译器根据函数实参的类型(可能会出现隐式类型转换),可推断出要调用的那个函数,如果有对应的函数就调用该函数,否则出现编译错误。
2.动态多态
动态多态(晚绑定):在程序执行期间(非编译期间)判断所引用对象的实际类型,根据其实际类型调相应的方法。 使用virtual关键字修饰类的成员函数时,指明该函数为虚函数,派生类需要重新实现。编译器将实现动态绑定。
动态绑定条件
- 必须是虚函数。
- 通过基类类的引用或者指针调用虚函数。
注意
- 这里是列表文本构造函数不能用virtual修饰。(对象还未构造,如何查虚表)
- static和virtual不能一起用。(静态函数没有this指针)
- 赋值运算符重载可以用virtual修饰,但不建议定义为虚函数。(在派生类和基类的赋值时可能会有问题)
- 友元函数不能用virtual修饰。(友元不能传递)
- 析构函数可以用virtual修饰。(当类内有虚函数时,最好把析构函数给成虚函数。或者派生类中有申请空间并释放空间的动作时,也给成虚函数)
纯虚函数
在成员函数的形参后面写上=0,则成员函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。纯虚函数在派生类中重新定义以后,派生类才能实现实例化出对象。
总结
- 派生类重写基类的虚函数实现多态,要求函数名、参数列表、返回值完全相同(协变除外)
- 基类中定义了虚函数,在派生类中该函数始终保持虚函数的特性。
- 只有类的非静态成员函数才能定义为虚函数,静态成员函数不能定义为虚函数。
- 如果在类外定义虚函数,只能在声明函数时加virtual关键字,定义时不用加。
- 构造函数不能定义为虚函数,虽然可以将赋值运算符重载函数定义为虚函数,但最好不要这么做,使用时容易混淆。
- 不要在构造函数和析构函数中调用虚函数,在构造函数和析构函数中,对象是不完整的,可能会出现未定义的行为。
- 最好将基类的析构函数声明为虚函数。(析构函数比较特殊,因为派生类的析构函数跟基类的析构函数名称不一样,但是构成覆盖,这里编译器做了特殊处理)
- 虚表是所有类对象实例公有的。
虚表剖析
- 对于有虚函数的类,编译器都会维护一张虚表,对象的前四个字节就是指向虚表的指针。
- 没有覆盖
class CBase {public: CBase(){m_iTest = 10;} virtual void FunTest0(){cout<<"CBase::FunTest0()";} virtual void FunTest1(){cout<<"CBase::FunTest1()";} virtual void FunTest2(){cout<<"CBase::FunTest2()";} private: int m_iTest;};class CDerived:public CBase {public: virtual void FunTest4(){cout<<"CDerived::FunTest4()";} virtual void FunTest5(){cout<<"CDerived::FunTest5()";} virtual void FunTest6(){cout<<"CDerived::FunTest6()";}};typedef void (*FUN_TEST)();void FunTest() { CBase base; cout<<"CBase vfptr:"<<endl; for (int iIdx = 0; iIdx < 3; ++iIdx) { FUN_TEST funTest = (FUN_TEST)(*((int*)*(int *)&base + iIdx)); funTest(); cout<< ": "<<(int *)funTest<<endl; } cout<<endl; CDerived derived; cout<< "CDerived vfptr:"<<endl; for (int iIdx = 0; iIdx < 6; ++iIdx) { FUN_TEST funTest = (FUN_TEST)(*((int*)*(int *)&derived + iIdx)); funTest(); cout<< ": "<<(int *)funTest<<endl; } }int main(){ FunTest(); return 0;}
基类中的构造函数:填写虚表 派生类:
- 先拷贝基类虚表。
- 若有重写,替换(覆盖)相同位置的函数。
- 后面添加自己特有的虚函数。 虚函数调用:先找到虚表,从表中找到调用的函数。
有覆盖
class CBase { public: virtual void FunTest0(){cout<<"CBase::FunTest0()"<<endl;} virtual void FunTest1(){cout<<"CBase::FunTest1()"<<endl;} virtual void FunTest2(){cout<<"CBase::FunTest2()"<<endl;} virtual void FunTest3(){cout<<"CBase::FunTest3()"<<endl;}};class CDerived:public CBase{public: virtual void FunTest0(){cout<<"CDerived::FunTest0()" <<endl;} virtual void FunTest1(){cout<<"CDerived::FunTest1()" <<endl;} virtual void FunTest4(){cout<<"CDerived::FunTest4()" <<endl;} virtual void FunTest5(){cout<<"CDerived::FunTest5()" <<endl;}};typedef void (*_pFunTest)();void FunTest(){ CBase base; for (int iIdx = 0; iIdx < 4; ++iIdx) { _pFunTest pFunTest = (_pFunTest)(*(( int*)*(int *)&base + iIdx)); pFunTest(); } cout<<endl; CDerived derived; for (int iIdx = 0; iIdx < 6; ++iIdx) { _pFunTest pFunTest = (_pFunTest)(*((int*)*(int *)&derived + iIdx)); pFunTest(); } }void TestVirtual(){ CBase base0; CDerived derived; CBase& base1 = derived; }int main() { FunTest(); TestVirtual(); return 0; }
多重继承:没有虚函数覆盖
class CBase0 {public: CBase0(){m_iTest = 0xB0;} virtual void PrintB0(){cout<<"m_iTest = "<<hex<<m_iTest<< " CBase0::PrintB0()" <<endl;} int m_iTest; };class CBase1 {public: CBase1(){m_iTest = 0xB1;} virtual void PrintB1(){cout<<"m_iTest = "<<hex<<m_iTest<< " CBase1::PrintB1()" <<endl;} int m_iTest; };class CBase2 { public: CBase2(){m_iTest = 0xB2;} virtual void PrintB2(){cout<<"m_iTest = "<<hex<<m_iTest<< " CBase2::PrintB2()" <<endl;} int m_iTest;};class CDerived:public CBase0, public CBase1, public CBase2 { public: CDerived(){m_iTest = 0xD0;} virtual void PrintD(){cout<<"m_iTest = "<<hex<<m_iTest<< " CDerived::PrintD()"<<endl;} int m_iTest; };typedef void (*VFTABLE_FUN)();void PrintVfPTab(char * _pStr, int *_pVfAddr) { cout<<_pStr<<endl; for (int iIdx = 0; ;iIdx++) { VFTABLE_FUN pPrintVTab = (VFTABLE_FUN)(*(_pVfAddr + iIdx)); if (NULL == pPrintVTab) { break; } pPrintVTab(); cout<<( int*)pPrintVTab<<endl; } cout<<endl;}void FunTest() { CDerived derived; cout<< sizeof(derived)<<endl; int *pVfTAddr = NULL; CBase0& base0 = derived; pVfTAddr = ( int*)(*(int *)&base0); PrintVfPTab( "CBase0 virtual Tab:", pVfTAddr); CBase1& base1 = derived; pVfTAddr = ( int*)(*(int *)&base1); PrintVfPTab( "CBase1 virtual Tab:", pVfTAddr); CBase2& base2 = derived; pVfTAddr = ( int*)(*(int *)&base2); PrintVfPTab( "CBase2 virtual Tab:", pVfTAddr); pVfTAddr = ( int*)(*(int *)&derived); PrintVfPTab( "CDerived virtual Tab:" , pVfTAddr); derived.PrintB0(); derived.PrintB1(); derived.PrintB2(); derived.PrintD();}
多重继承:有虚函数覆盖
class CBase0 {public: CBase0(){m_iTest = 0xA0;} virtual void Print(){cout<<"m_iTest = "<<hex<<m_iTest<< " CBase2::Print()"<<endl;} int m_iTest;};class CBase1 {public: CBase1(){m_iTest = 0xB0;} virtual void Print(){cout<<"m_iTest = "<<hex<<m_iTest<< " CBase2::Print()"<<endl;} int m_iTest;};class CBase2{public: CBase2(){m_iTest = 0xC0;} virtual void Print(){cout<<"m_iTest = "<<hex<<m_iTest<< " CBase2::Print()"<<endl;} int m_iTest; };class CDerived:public CBase0, public CBase1, public CBase2 { public: CDerived(){m_iTest = 0xD0;} virtual void Print(){cout<<"m_iTest = "<<hex<<m_iTest<< " CDerived::Print()"<<endl;} int m_iTest;};void FunTest() { CDerived derived; cout<< sizeof(derived)<<endl; CBase0& base0 = derived; base0.Print(); CBase1& base1 = derived; base1.Print(); CBase2& base2 = derived; base2.Print(); derived.Print(); }
若有多个虚表,则自己的虚拟函数加在第一个虚表的末尾。
0 0
- C++多态详谈
- c--sizeof详谈
- 详谈C/C++内存分配
- 详谈Objective-C内存管理机制
- Java面向对象详谈——封装、继承和多态
- 老生综合详谈C语言关键字、内存分配、数据存储
- JDBC 详谈
- 详谈SGA
- UML详谈
- StringTemplate详谈
- JVM 详谈
- 委托详谈
- 详谈架构
- 详谈UINavigationController
- UITableView详谈
- const详谈
- 详谈继承
- 继承详谈
- Scala基础学习
- hdu 2014
- .classpath文件有什么用?http://blog.csdn.net/shichaosong/article/details/9011695
- eclipse validation功能
- 【HTML】5.表单标签
- C++多态详谈
- unity之VR Infinite Gesture插件学习01
- Python之操作MySQL数据库
- eclipse 编译程序提示Unsupported major.minor version 52.0
- 集合框架
- hdu 2015
- phpqrcode 生成二维码
- Linux达人养成计划一
- try_to_wake_up函数