c++ 多态/虚表剖析
2016-11-13 10:45
399 查看
【[b]对象的类型】[/b]
静态类型:对象声明时的类型,是在编译时确定的
动态类型:目前所指对象的类型,是在运行时确定的
【多态】
多态:一词最初来源于希腊语,意思是具有多种形式或形态的情形,在c++语言中多态有着更广泛的含义
静态多态:编译器在编译期间完成的,编译器根据函数实参的类型可能会进行隐式类型转换),可推断出要调用那个函数,如果有对应的函数有调用该函数,否则出现编译错误。
【动态多态】
多态绑定:在程序执行期间(非编译器)判断所引用对象的实际类型,根据其实际类型调用相应的方法。
使用virtual关键字修饰类的成员函数时, 指明该函数为虚函数,派生类需要重新实现,编译器将实现动态绑定。
【动态绑定条件】
1.必须是虚函数
2.通过基类类型的引用或者指针调用虚函数
【继承体系同名成员函数的关系】
1.重载:
在同一作用域;
函数名相同、参数不同;
返回值可以不同。
2.重写(覆盖):
不在同一作用域(分别在基类和派生类);
函数名相同/参数相同/返回值相同(协变(基类返回基类指针,派生类返回派生类指针)例外);
基类函数必须有virtual关键字;
访问修饰符可以不同。
3.重定义(隐藏):
在不同作用域中(分别在基类和派生类);
函数名相同;
在基类和派生类中只要不构成重写就是重定义。
【纯虚函数】
在成员函数的形参后面写上=0,则成员函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。纯虚函数在派生类中重新定义以后,派生类才能实例化出对象。
1.派生类重写基类的虚函数实现多态,要求函数名、参数列表、返回值完全相同。(协变除外)
2.基类中定义了虚函数,在派生类中该函数始终保持虚函数的特性。
3.只有类的非静态成员函数才能定义为虚函数,静态成员函数不能定义为虚函数。
4.如果在类外定义虚函数,只能在声明函数时附加virtual关键字,定义时不用加。
5.构造函数不能定义为虚函数,虽然可以将operator=定义为虚函数,但最好不要这么做,使用时容易混淆。
6.不要在构造函数和析构函数中调用虚函数,在构造函数和析构函数中,对象时不完整的,可能会出现未定义的行为。
7.最好将基类的析构函数声明为虚函数。(析构函数比较特殊,因为派生类的析构函数跟基类的析构函数名称不一样,但是构成覆盖,这里编译器做了特殊处理)
8.虚表是所有类对象实例共用的。
【虚表剖析】
对于有虚函数的类,编译器都会维护一张虚表,对象的前四个字节就是指向虚表的指针。
【没有覆盖】
1、虚函数按照其声明顺序存在于虚表中
2、在派生类中,前面是基类的虚函数,后面是派生类中虚函数
【有覆盖】
【多重继承:没有虚函数覆盖】
【多重继承:有虚函数覆盖】
因为Print()为虚函数,CDerived的虚表中覆盖了基类的虚函数,所以每次调用都调用的是CDeried中的print函数。
静态类型:对象声明时的类型,是在编译时确定的
动态类型:目前所指对象的类型,是在运行时确定的
【多态】
多态:一词最初来源于希腊语,意思是具有多种形式或形态的情形,在c++语言中多态有着更广泛的含义
静态多态:编译器在编译期间完成的,编译器根据函数实参的类型可能会进行隐式类型转换),可推断出要调用那个函数,如果有对应的函数有调用该函数,否则出现编译错误。
【动态多态】
多态绑定:在程序执行期间(非编译器)判断所引用对象的实际类型,根据其实际类型调用相应的方法。
使用virtual关键字修饰类的成员函数时, 指明该函数为虚函数,派生类需要重新实现,编译器将实现动态绑定。
class CWashRoom { public: void GoToManWashRoom() { cout << "Man--->Please Left" << endl; } void GoToWomanWashRoom() { cout << "Woman--->Please Right" << endl; } }; class CPerson { public: virtual void GoToWashRoom(CWashRoom & _washRoom) = 0; }; class CMan :public CPerson { public: virtual void GoToWashRoom(CWashRoom & _washRoom) { _washRoom.GoToManWashRoom(); } }; class CWoman :public CPerson { public: virtual void GoToWashRoom(CWashRoom & _washRoom) { _washRoom.GoToWomanWashRoom(); } }; void FunTest() { CWashRoom washRoom; for (int iIdx = 1; iIdx <= 10; ++iIdx) { CPerson* pPerson; int iPerson = rand() % iIdx; if (iPerson & 0x01) { pPerson = new CMan; } else { pPerson = new CWoman; } pPerson->GoToWashRoom(washRoom); delete pPerson; pPerson = NULL; Sleep(1000); } }
【动态绑定条件】
1.必须是虚函数
2.通过基类类型的引用或者指针调用虚函数
【继承体系同名成员函数的关系】
1.重载:
在同一作用域;
函数名相同、参数不同;
返回值可以不同。
2.重写(覆盖):
不在同一作用域(分别在基类和派生类);
函数名相同/参数相同/返回值相同(协变(基类返回基类指针,派生类返回派生类指针)例外);
基类函数必须有virtual关键字;
访问修饰符可以不同。
3.重定义(隐藏):
在不同作用域中(分别在基类和派生类);
函数名相同;
在基类和派生类中只要不构成重写就是重定义。
【纯虚函数】
在成员函数的形参后面写上=0,则成员函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。纯虚函数在派生类中重新定义以后,派生类才能实例化出对象。
virtual void Display () = 0; // 纯虚函数
1.派生类重写基类的虚函数实现多态,要求函数名、参数列表、返回值完全相同。(协变除外)
2.基类中定义了虚函数,在派生类中该函数始终保持虚函数的特性。
3.只有类的非静态成员函数才能定义为虚函数,静态成员函数不能定义为虚函数。
4.如果在类外定义虚函数,只能在声明函数时附加virtual关键字,定义时不用加。
5.构造函数不能定义为虚函数,虽然可以将operator=定义为虚函数,但最好不要这么做,使用时容易混淆。
6.不要在构造函数和析构函数中调用虚函数,在构造函数和析构函数中,对象时不完整的,可能会出现未定义的行为。
7.最好将基类的析构函数声明为虚函数。(析构函数比较特殊,因为派生类的析构函数跟基类的析构函数名称不一样,但是构成覆盖,这里编译器做了特殊处理)
8.虚表是所有类对象实例共用的。
【虚表剖析】
class CTest { public: CTest() { iTest = 10; cout << "this =" << this << endl; } virtual ~CTest(){}; private: int iTest; }; int main() { { CTest test; cout << sizeof(test) << endl; } }
对于有虚函数的类,编译器都会维护一张虚表,对象的前四个字节就是指向虚表的指针。
【没有覆盖】
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; }说明:
1、虚函数按照其声明顺序存在于虚表中
2、在派生类中,前面是基类的虚函数,后面是派生类中虚函数
【有覆盖】
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(); } int main() { FunTest(); return 0; }
【多重继承:有虚函数覆盖】
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(); } int main() { FunTest(); return 0; }
因为Print()为虚函数,CDerived的虚表中覆盖了基类的虚函数,所以每次调用都调用的是CDeried中的print函数。
相关文章推荐
- C++多态及虚表深度剖析
- c++ 中多态总结以及虚表剖析
- 用C实现C++的多态---剖析GTK的"对象" (三)
- 深入剖析C++继承,多态以及隐藏(一)。(虚函数探究)
- C++多态深度剖析
- C++三大特性之多态(二)---深度剖析各种虚继承虚函数以及虚表的内容存放
- 用C实现C++的多态---剖析GTK的"对象" (一)
- C++多态深度剖析
- 剖析C++多态:用C实现简单多态
- C++运行时多态之虚表
- c++中虚函数继承,虚表剖析
- 深入剖析C++继承,多态以及隐藏(二)。(纯虚函数以及重写与隐藏)
- C++深度剖析(一) this指针与虚表
- c++实现多态的方法 虚表
- C++多态:从虚表指针到设计模式
- 用C实现C++的多态---剖析GTK的"对象" (四)
- 深入剖析C++继承,多态以及隐藏(三)(类层次中的转化问题)
- 虚指针 虚表之c++实现多态的方法
- C++多态之动态多态:虚函数,虚表,动态联编
- C++ 多态深度剖析