C++多态

来源:互联网 发布:linux网络运维 编辑:程序博客网 时间:2024/06/05 05:36

一、C++多态
多态是指一组具有继承关系的类,拥有相同的接口(函数名、形参和返回值),并允许有各自不同的实现,且一个对象实例只有在调用这共同接口的时候,才能确定调用的是何种实现。 即,“一个接口,多种实现”

二、静态多态
编译器在编译期间完成的
静态多态 : 通过”彼此单独定义但支持共同操作的具体类“来表达共同性。
函数多态:即函数重载
概念:基于不同的参数列表,同一函数名可指向不同的函数体实现。

三、动态多态
动态绑定:在程序执行期间判断所引用对象的实际类型,根据其实际类型调用相应的方法。
以类继承和虚函数机制为基础,表达共同接口。
动态多态是为面向对象编程服务的。因此,多态通常是指动态多态。

1、使用virtual关键字:
使用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 Test(){    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);    }}

2、虚函数的声明:
虚函数: 虚函数必须是被关键字virtual修饰的某类的非静态成员函数。
声明方法: virtual <函数返回值类型> <函数名>(<参数表>);
注意: 在派生类中重定义的虚函数必须和基类的虚函数完全相同(协变除外),即具有相同的返回类型、参数个数和参数类型。否则就是一种语法错误。
3、继承体系中容易搞混的三种同名函数的关系:

4、虚函数的总结:
为什么静态成员函数不能定义为虚函数
为什么构造函数不能定义为虚函数,为什么operator=可以定义成虚函数,但又不建议这样做


6、分析为什么构造函数和析构函数中不能调用虚函数,以及调用之后存在的问题

7、最好将析构函数声明为虚函数,为什么?

class Base{public:     Base* func()    {        cout << "Base::func1" << endl;        return this;    }    virtual~Base()    //~Base()    {        cout << "~Base()" << endl;    }};class Derive :public Base{public:    Derive* func()    {        cout << "Derive::func1" << endl;        return this;    }    ~Derive()    {        cout << "~Derive()" << endl;    }};int main(){    Base* ptr = new Derive;    delete ptr;    getchar();    return 0;}

class Base{public:    Base()    {        cout << "Base()" << endl;    }    ~Base()    {        cout << "~Base()" << endl;    }private:    int _b;};class Derived :public Base{public:    Derived()    {        cout << "Derived()" << endl;        p = new int(0);    }    ~Derived()    {        cout << "Derived()" << endl;        delete p;    }private:    int _d;    int* p;};void Test(){    Base* pb = new Derived;//如果析构函数不是虚函数,那么就是普通函数的调用方法,直接调用,不会去查函数虚表,这样pb是基类的指针,它只会调用基类的析构函数    delete pb;}int main(){    Test();    getchar();    return 0;}

验证结果:


四、虚函数表vftable(virtual function table):

先来看一段代码:

class Base{public:    virtual void FunTest1()    {        cout << "Base::FunTest1()" << endl;    }private:    int _b;};class Derived :public Base{public:    virtual void FunTest1()    {        cout << "Derived::FunTest1()" << endl;    }private:    int _d;};int main(){    cout << sizeof(Derived) << endl;    return 0;}


按照以前的算法 sizeof(D)是8,个字节,那么多出来的4个字节又是什么呢?

调用监视窗口可以看到有两个指针 _vfptr (虚表指针)

1、普通继承(单):

class B{publicvirtual void FunTest1()    {        cout << "B::FunTest1()" << endl;    }    virtual void FunTest2()    {        cout << "B::FunTest2()" << endl;    }    int _b;};class C :public B{public:    virtual void FunTest1()    {        cout << "C::FunTest1()" << endl;    }    virtual void FunTest3()    {        cout << "C::FunTest3()" << endl;    }    int _c;};void Test(B& b){    b.FunTest1();    b.FunTest2();}int main(){    B b;    C c;    Test(b);    Test(c);    b._b = 1;    c._c = 2;    cout << sizeof(C) << endl;//12    getchar();    return 0;}

2、多继承:

class B1{public:    virtual void FunTest1()    {        cout << "B1::FunTest1()" << endl;    }    virtual void FunTest2()    {        cout << "B1::FunTest2()" << endl;    }    int _b1;};class B2{public:    virtual void FunTest3()    {        cout << "B2::FunTest3()" << endl;    }    virtual void FunTest4()    {        cout << "B2::FunTest4()" << endl;    }    int _b2;};class C :public B1, public B2{public:    virtual void FunTest1()    {        cout << "C::FunTest1()" << endl;    }    virtual void FunTest3()    {        cout << "C::FunTest3()" << endl;    }    virtual void FunTest5()    {        cout << "C::FunTest5()" << endl;    }    int _c;};int main(){    cout << sizeof(C) << endl;//20    B1 b1;    B2 b2;    C c;    B1* p1 = &b1;    p1->FunTest1();    p1->FunTest2();    cout << endl;    B2* p2 = &b2;    p2->FunTest3();    p2->FunTest4();    cout << endl;    C* pc = &c;    pc->FunTest3();    pc->FunTest5();    getchar();    return 0;}

这里写图片描述

3、虚拟继承:

class B{public:    virtual void FunTest1()    {        cout << "B::FunTest1()" << endl;    }    virtual void FunTest2()    {        cout << "B::FunTest2()" << endl;    }    int b;};class C :virtual public B{public:    virtual void FunTest1()    {        cout << "C::FunTest1()" << endl;    }    virtual void FunTest3()    {        cout << "C::FunTest3()" << endl;    }    int c;};int main(){    cout << sizeof(C) << endl;//20    B b;    C c;    b.b = 1;    c.c = 2;    B* p1 = &b;    p1->FunTest1();    p1->FunTest2();    cout << endl;    C* pc = &c;    pc->FunTest1();    pc->FunTest3();    getchar();    return 0;}


4、菱形继承:

class B{public:    virtual void FunTest1()    {        cout << "B::FunTest1()" << endl;    }    virtual void FunTest2()    {        cout << "B::FunTest2()" << endl;    }    int b;};class C1 :virtual public B{public:    virtual void FunTest1()    {        cout << "C1::FunTest1()" << endl;    }    virtual void FunTest3()    {        cout << "C1::FunTest3()" << endl;    }    int c1;};class C2 :virtual public B{public:    virtual void FunTest1()    {        cout << "C2::FunTest1()" << endl;    }    virtual void FunTest4()    {        cout << "C2::FunTest4()" << endl;    }    int c2;};class D :public C1, public C2{public:    virtual void FunTest1()    {        cout << "D::FunTest1()" << endl;    }    virtual void FunTest3()    {        cout << "D::FunTest3()" << endl;    }    virtual void FunTest5()    {        cout << "D::FunTest5()" << endl;    }    int d;};int main(){    cout << sizeof(D) << endl;    getchar();    return 0;}


这次为什么D的大小是36 呢?
这时我们就需要分析其对象模型

虚表指针:
每一个多态类都有且仅有一个存储所有虚函数入口地址的数组。
虚函数表指针vptr:
这里写图片描述
每一个多态类的所有对象都有且仅有一个的隐藏成员vptr,指向虚函数表vftable.
vptr和vftable的初始化:
基类对象的vptr指向基类的vftable。
派生类对象vptr指向该派生类的vftable。
如果派生类没有覆盖(override)父类的虚函数,则其vftable中对应表项指向其父类的此函数。 如果派生类覆盖了父类的虚函数,则vftable中对应表项指向重写后的此函数。 如果派生类定义了新的虚函数,则此函数的地址将被添加到vftable中
虚函数的工作原理:
多态的函数调用语句被编译成一系列根据基类指针所指向的(或基类引用所引用的)对象中存放的虚函数表的地址,在虚函数表中查找虚函数地址,并调用虚函数的过程。
vptr和vftable在内存中的存储

class Base{public:    virtual void func1()    {        cout << "Base::func1" << endl;    }    virtual void func2()    {        cout << "Base::func2" << endl;    }    void func3()    {        cout << "Base::func3" << endl;    }};class Derive :public Base{public:    virtual void func1()    {        cout << "Derive::func1" << endl;    }    virtual void func3()//构成重载   func也要放在虚表中    {        cout << "Derive::func3" << endl;    }    virtual void func4()    {        cout << "Derive::func3" << endl;    }};typedef void(*V_FUNC)();void PrintVTable(int vtable)//打印虚表{    int* vf_array = (int*)vtable;    printf("vtable:0x%p\n",vtable);    for (size_t i = 0; vf_array[i] != 0; ++i)    {        printf("vatable[%d]:0x%p->", i, vf_array[i]);        V_FUNC f = (V_FUNC)vf_array[i];        f();    }    printf("----------------------------\n");}int main(){    Base b;    Derive d;    PrintVTable(*(int*)&b);    PrintVTable(*(int*)&d);    Base* ptr=&d;    ptr->func1();//构成多态    ptr->func3();//不是多态    getchar();    return 0;}