多态和多态模型

来源:互联网 发布:列举常用的查找算法 编辑:程序博客网 时间:2024/06/03 09:40

1:什么是多态?
1.1 概念回顾
虚函数:类的成员函数前面加virtual关键字,则这个成员函数称为虚函数。
虚函数重写:当在子类中定义了一个与父类完全相同的虚函数时,则称子类的这个函数重写(也称覆盖)了父类的这个虚函数。
多态:当使用基类的指针或引用调用重写的虚函数时,当指向父类调用父类的虚函数,当指向子类调用子类的虚函数。
这里写图片描述
注:
1)派生类重写基类的虚函数实现多态,要求函数名、参数列表、返回值完全相同。(协变除外)
2)基类中定义了虚函数,在派生类中该函数始终保持虚函数的特性。
3)只有类的成员函数才能定义为虚函数。
4)静态成员函数不能定义为虚函数。
5)如果在类外定义虚函数,只能在声明函数时加virtual,类外定义函数时不能加virtual。
6)构造函数不能为虚函数,虽然可以将operator=定义为虚函数,但是最后不要将operator=定义为虚函数,因为容易使用时引起混肴。
7)不要在构造函数和析构函数里面调用虚函数,在构造函数和析构函数中,对象是不完整的,可能会发生未定义的行为。
8)最好不要把基类的析构函数声明为虚函数。(因为,析构函数比较特殊,派生类的析构函数与基类的析构函数名称不一样,但由于编译器的特殊处理会把析构函数的名称都翻译为deconstruct,就构成覆盖。)
c++中虚函数的主要作用就是去实现多态。即父类的指针/引用调用重写的虚函数,当父类指针/引用指向父类对象调用父类的虚函数,指向子类调用子类的虚函数。
1.2探索虚函数表
虚函数表是通过一块连续内存来存储函数的地址。这张表解决了继承、虚函数(重写)的问题。在有虚函数的对象实例中都存在一张虚函数表,虚函数表就像一张地图,指明了实际应该调用的虚函数函数。

class Base{ public:     virtual void func1()     {}     virtual void func2()     {} private:     int a; };void Test1(){    Base b1;    cout<<sizeof(b1)<<endl;}    

分析如下:
这里写图片描述
由上图可知,Base类的大小由成员变量b1的内存大小和虚表指针所占内存大小决定,故而在32位程序中,b1占8个字节,在64位程序中,b1占12个字节。
1.3 多态的分类——动/静态多态
多态是多种形态,C++的多态分为静态多态和动态多态。
1)静态多态就是重载,因为在编译期间决议确定的,所以称为静态多态。
2)动态多态就是通过继承重写基类的虚函数实现的多态,因为是在运行时决议确定的,所以称为动态多态。

#include<iostream>using namespace std;class Base{public:    virtual void func1()    {        cout << "Base::func1" << endl;    }    virtual void func2()    {        cout << "Base::func2" << endl;    }    void func3()    {        cout << "Base::func3" << endl;    }    void func3(int n)    {        cout <<n<<"->" "Base::func3" << endl;    }private:    int a;};class Derive :public Base{public:    virtual void func1()    {        cout << "Derive::func1" << endl;    }private:    int b;};void Test(Base& p){    p.func1();    p.func3();    p.func3(100);}int main(){    Base b;    Derive d;    Test(b);    Test(d);    system("pause");    return 0;}

这里写图片描述
注意:
基类指针或引用+虚函数=动态联编
动态联编+虚函数重写=多态
动态联编效率不及静态联编,动态联编指令多。
例题:

Base* p=NULL;p->func1();//动态联编,要根据p指向的地址寻找虚表,对p解引用了,崩p->func3();//静态联编,没有对p解引用p->a=100;//对p解引用了,崩

2:多态的对象模型–单继承&多继承?
2.1 单继承:

//单继承多态对象模型#include<iostream>using namespace std;class Base{public:    virtual void func1()    {        cout << "Base::func1" << endl;    }    virtual void func2()    {        cout << "Base::func2" << endl;    }private:    int a;};class Derive :public Base{public:    virtual void func1()    {        cout << "Derive::func1" << endl;    }    virtual void func3()    {        cout << "Derive::func3" << endl;    }    virtual void func4()    {        cout << "Derive::func4" << endl;    }private:    int b;};typedef void(*FUNC)();void PrintVTable(int** VTable){    cout << "====================================" << endl;    cout << "虚表地址>" << VTable << endl;    for (size_t i = 0; VTable[i] != 0; ++i)    {        printf("Vfunc[%d]:%p->", i, VTable[i]);        FUNC f = (FUNC)VTable[i];        f();    }    cout << "====================================" << endl;}void Test1(){    Base b1;    Derive d1;    int** VTable1 = (int**)(*(int**)&b1);    int** VTable2 = (int**)(*(int**)&d1);    PrintVTable(VTable1);    PrintVTable(VTable2);}int main(){    Test1();    system("pause");    return 0;}

运行结果如下:
这里写图片描述
分析如下图:
这里写图片描述
2.2 多继承:

//多继承多态对象模型#include<iostream>using namespace std;class Base1{public:    virtual void func1()    {        cout << "Base1::func1" << endl;    }    virtual void func2()    {        cout << "Base1::func2" << endl;    }private:    int b1;};class Base2{public:    virtual void func1()    {        cout << "Base2::func1" << endl;    }    virtual void func2()    {        cout << "Base2::func2" << endl;    }private:    int b2;};class Derive :public Base1,public Base2{public:    virtual void func1()    {        cout << "Derive::func1" << endl;    }    virtual void func3()    {        cout << "Derive::func3" << endl;    }   private:    int d;};typedef void(*FUNC)();void PrintVTable(int** VTable){    cout << "====================================" << endl;    cout << "虚表地址>" << VTable << endl;    for (size_t i = 0; VTable[i] != 0; ++i)    {        printf("Vfunc[%d]:%p->", i, VTable[i]);        FUNC f = (FUNC)VTable[i];        f();    }    cout << "====================================" << endl;}void Test1(){    Derive d1;    int** VTable2 = (int**)(*(int**)&d1);    PrintVTable(VTable2);    //Base2虚表在对象Base1后    VTable2 = (int**)(*((int**)&d1+sizeof(Base1)/4));    PrintVTable(VTable2);}int main(){    Test1();    system("pause");    return 0;}

运行结果如下:
这里写图片描述
分析图如下:
这里写图片描述

3:多态的对象模型–菱形继承和菱形虚拟继承?(选做)
3.1 菱形继承

#include<iostream>using namespace std;class Base{public:    virtual void func1()    {        cout << "Base::func1" << endl;    }    virtual void func2()    {        cout << "Base::func2" << endl;    }private:    int b;};class Base1:public Base{public:    virtual void func1()    {        cout << "Base1::func1" << endl;    }    virtual void func3()    {        cout << "Base1::func3" << endl;    }    virtual void func4()    {        cout << "Base1::func4" << endl;    }private:    int b1;};class Base2 :public Base{public:    virtual void func1()    {        cout << "Base2::func1" << endl;    }    virtual void func5()    {        cout << "Base2::func5" << endl;    }    virtual void func6()    {        cout << "Base2::func6" << endl;    }private:    int b2;};class Derive :public Base1, public Base2{public:    virtual void func1()    {        cout << "Derive::func1" << endl;    }    virtual void func3()    {        cout << "Derive::func3" << endl;    }    virtual void func5()    {        cout << "Derive::func5" << endl;    }    virtual void func7()    {        cout << "Derive::func7" << endl;    }private:    int d;};typedef void(*FUNC)();void PrintVTable(int** VTable){    cout << "====================================" << endl;    cout << "虚表地址>" << VTable << endl;    for (size_t i = 0; VTable[i] != 0; ++i)    {        printf("Vfunc[%d]:%p->", i, VTable[i]);        FUNC f = (FUNC)VTable[i];        f();    }    cout << "====================================" << endl;}void Test1(){    Derive d1;    int** VTable2 = (int**)(*(int**)&d1);    PrintVTable(VTable2);    //Base2虚表在对象Base1后    VTable2 = (int**)(*((int**)&d1 + sizeof(Base1) / 4));    PrintVTable(VTable2);}int main(){    Test1();    system("pause");    return 0;}

分析图如下:
这里写图片描述
这里写图片描述
我们可以看见,最顶端的父类Base其成员变量b存在于Base1和Base2中,并被Derive给继承下去了。而在Derive中,其有Base1和Base2的实例,于是Base的成员在Derive的实例中存在两份,一份是Base1继承而来的,另一份是Base2继承而来的。所以,如果我们使用以下语句,则会产生二义性编译错误:

Derive d;d.b = 0; //二义性错误d.Base1::d = 1; //正确d.Base2::d = 2; //正确

注意,上面例程中的最后两条语句存取的是两个变量。虽然我们消除了二义性的编译错误,但Base类在Derive中还是有两个实例,这种继承造成了数据的重复,我们叫这种继承为重复继承。重复的基类数据成员可能并不是我们想要的。所以,C++引入了虚基类的概念。
3.2 菱形虚拟继承
这里写图片描述
上述的“菱形继承”只需要把Base1和Base2继承Base的语法中加上virtual 关键,就成了菱形虚拟继承。结构如上图,其省略后的源码如下所示:

class Base{……};class Base1 : virtual public Base1{……};class Base2: virtual public Base2{……};class D : public B1, public B2{ …… };

Base是基类,里有虚函数,故而一定有虚表;Base1和Base2当有虚函数时才有虚表,按继承顺序放置,当Base1和Base2只有一个有虚函数,则有虚函数的在前。
菱形虚继承打印虚表的运行结果如下:
这里写图片描述
菱形虚继承的虚基表如下所示:
这里写图片描述
菱形继承和菱形虚继承的虚基表区别如下图:
这里写图片描述
采用虚继承解决了菱形继承的冗余和二义性问题。