多态中虚函数的简单理解

来源:互联网 发布:仙知机器人 冯源 编辑:程序博客网 时间:2024/06/10 04:07

虚函数

在之前,我曾经在多态中简单的提到过虚函数,虚函数就是用virtual关键字去修饰类的成员函数,然后将这个函数在派生类中去重新实现。

以代码为例:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>using namespace std;class B{public:virtual void Test1(int i){cout << "B:Test1()" << endl;}virtual void Test2(int i){cout << "B:Test2()" << endl;}void Test3(int i){cout << "B:Test3()" << endl;}};class C: public B{public:virtual void Test1(int i){cout << "C:Test1()" << endl;}void Test2(int i){cout << "C:Test2()" << endl;}virtual void Test3(int i){cout << "C:Test3()" << endl;}};int main(){B* b = new C;b->Test1(0);b->Test2(0);b->Test3(0);return 0;}

其中Test1就是进行了重写的虚函数,而Test2没有进行重写,Test3则是不是虚函数,所以这里的Test2和Test3都没有实现动态多态,只有Test1实现了动态多态。

之前强调过的我在这里再强调一遍,动态多态有两个条件

①必须是虚函数;②必须通过基类类型的指针或引用进行调用

纯虚函数

介绍了虚函数,那么就得提到纯虚函数。纯虚函数就是在成员函数的形参后面写上=0,那么成员函数就是纯虚函数。而包含纯虚函数的类就叫做抽象类(又叫接口类)。但是需要注意的是,抽象类不能实例化出对象。纯虚函数在派生类中重新定义以后,派生类才能实例化出对象

上面说了虚函数,大体的解释了一下虚函数的使用方式,但是,在类中使用虚函数的时候,有一些注意事项你还是需要注意的:

①派生类重写基类中的虚函数实现多态,函数名,参数列表,返回值全都要一样。(协变除外,协变是返回值不一样);

②派生类中重写的虚函数要始终保持与基类中虚函数的特性一致;

③只有类中的非静态成员函数才能定义为虚函数,静态成员函数不能定义为虚函数;

④构造函数不能定义为虚函数,但是最好将基类的析构函数声明为虚函数,另外operate=定义为虚函数也是可以的,但是最好不要这样做,容易导致混淆;

⑤不要在构造函数和析构函数中调用虚函数,在构造函数和析构函数中,对象是不完整的,可能会出现未定义的行为;

⑥如果在类外定义虚函数,只能在声明函数时加virtual关键字,定义时不用加;

⑦虚表是所有类对象实例共用的。

虚表

既然在上面的注意事项中写到了虚表,那么在这里就得说一下虚表并进行虚表剖析。

对于有虚函数的类,编译器都会维护一张虚表,对象的前四个字节就是指向虚表的指针

以代码为例:

class Test{public:Test(){i = 10;cout << "this = " << this << endl;}virtual ~Test(){}private:int i;};int main(){Test t;cout << sizeof(t) << endl;return 0;}

通过这个图片我们可以发现,在创建带有虚函数的类时,编译器会帮助维护一张虚表,其中对象的前四个字节就是指向虚表的指针

下面我们将分析没有覆盖和有覆盖两种情况。

没有覆盖:

代码:

class Base{public:Base(){ i = 10; }virtual void FunTest0(){ cout << "Base::FunTest0()"; }virtual void FunTest1(){ cout << "Base::FunTest1()"; }virtual void FunTest2(){ cout << "Base::FunTest2()"; }private:int i;};class Derived :public Base{public:virtual void FunTest4(){ cout << "Derived::FunTest4()"; }virtual void FunTest5(){ cout << "Derived::FunTest5()"; }virtual void FunTest6(){ cout << "Derived::FunTest6()"; }};typedef void(*FUN_TEST)();void FunTest(){Base b;cout << "Base vfptr:" << endl;for (int i = 0; i < 3; ++i){FUN_TEST funTest = (FUN_TEST)(*((int*)*(int *)&b + i));funTest();cout << ": " << (int *)funTest << endl;}cout << endl;Derived d;cout << "Derived vfptr:" << endl;for (int i = 0; i < 6; ++i){FUN_TEST funTest = (FUN_TEST)(*((int*)*(int *)&d + i));funTest();cout << ": " << (int *)funTest << endl;}}int main(){FunTest();return 0;}

由代码进行分析:

有覆盖:

代码:

class Base{public:virtual void FunTest0(){ cout << "Base::FunTest0()" << endl; }virtual void FunTest1(){ cout << "Base::FunTest1()" << endl; }virtual void FunTest2(){ cout << "Base::FunTest2()" << endl; }virtual void FunTest3(){ cout << "Base::FunTest3()" << endl; }};class Derived :public Base{public:virtual void FunTest0(){ cout << "Derived::FunTest0()" << endl; }virtual void FunTest1(){ cout << "Derived::FunTest1()" << endl; }virtual void FunTest4(){ cout << "Derived::FunTest4()" << endl; }virtual void FunTest5(){ cout << "Derived::FunTest5()" << endl; }};typedef void(*_pFunTest)();void FunTest(){Base base;for (int i = 0; i < 4; ++i){_pFunTest  pFunTest = (_pFunTest)(*((int*)*(int *)&base + i));pFunTest();}cout << endl;Derived derived;for (int i = 0; i < 6; ++i){_pFunTest  pFunTest = (_pFunTest)(*((int*)*(int *)&derived + i));pFunTest();}}void TestVirtual(){Base base0;Derived derived;Base& base1 = derived;}int main(){FunTest();TestVirtual();return 0;}
由代码进行分析:



下面我再简单剖析一下多重继承中有无虚函数覆盖的情况:

多重继承:没有虚函数覆盖

代码:

class Base0{public:Base0(){ i = 0xB0; }virtual void PrintB0(){ cout << "i = " << hex << i << "  Base0::PrintB0()" << endl;}int i;};class Base1{public:Base1(){ i = 0xB1;}virtual void PrintB1(){ cout << "i = " << hex << i << "  Base1::PrintB1()" << endl; }int i;};class Base2{public:          Base2(){ i = 0xB2; }virtual void PrintB2(){ cout << "i = " << hex << i << "  Base2::PrintB2()" << endl;}int i;};class Derived :public Base0, public Base1, public Base2{public:Derived(){ i = 0xD0;}virtual void PrintD(){ cout << "i = " << hex << i << "  Derived::PrintD()" << endl;}int i;};typedef void(*VFTABLE_FUN)();void PrintVfPTab(char * _pStr, int *_pVfAddr){cout << _pStr << endl;for (int i = 0;; i++){VFTABLE_FUN pPrintVTab = (VFTABLE_FUN)(*(_pVfAddr + i));if (NULL == pPrintVTab){break;}pPrintVTab();cout << (int*)pPrintVTab << endl;}cout << endl;}void FunTest(){Derived derived;cout << sizeof(derived) << endl;int *pVfTAddr = NULL;Base0& base0 = derived;pVfTAddr = (int*)(*(int *)&base0);PrintVfPTab("Base0 virtual Tab:", pVfTAddr);Base1& base1 = derived;pVfTAddr = (int*)(*(int *)&base1);PrintVfPTab("Base1 virtual Tab:", pVfTAddr);Base2& base2 = derived;pVfTAddr = (int*)(*(int *)&base2);PrintVfPTab("Base2 virtual Tab:", pVfTAddr);pVfTAddr = (int*)(*(int *)&derived);PrintVfPTab("Derived virtual Tab:", pVfTAddr);derived.PrintB0();derived.PrintB1();derived.PrintB2();derived.PrintD();}int main(){FunTest();return 0;}
由代码进行分析:


多重继承:有虚函数覆盖

代码:

class Base0{public:Base0(){ i = 0xA0; }virtual void Print(){ cout << "i = " << hex << i << "  Base2::Print()" << endl; }int i;};class Base1{public:Base1(){ i = 0xB0; }virtual void Print(){ cout << "i = " << hex << i << "  Base2::Print()" << endl; }int i;};class Base2{public:Base2(){ i = 0xC0; }virtual void Print(){ cout << "i = " << hex << i << "  Base2::Print()" << endl; }int i;};class Derived :public Base0, public Base1, public Base2{public:Derived(){ i = 0xD0; }virtual void Print(){ cout << "i = " << hex << i << "  Derived::Print()" << endl; }int i;};void FunTest(){Derived derived;cout << sizeof(derived) << endl;Base0& base0 = derived;base0.Print();Base1& base1 = derived;base1.Print();Base2& base2 = derived;base2.Print();derived.Print();}
由代码进行分析:



注:以上代码全是放在VS2013下进行调试的。













0 0
原创粉丝点击