C++之多态

多态

多态就是指一种事物在不同时期的不同体现。

多态有两张情况,动态多态静态多态

静态多态
在编译期间完成的,编译器根据函数实参的类型(可能会进行隐式类型转换),可推断出要调用那个函数,如果有对应的函数就调用该函数,否则出现编译错误。

静态多态的实现方法有两种:

1.重载
#include <iostream>

using namespace std;

int add(int left, int right)
{
    return left + right;
}

float add(float left, float right)
{
    return left + right;
}

int main()
{
    cout << add(10, 20) << endl;
    cout << add(3.13f, 2.14f) << endl;
    system("pause");
    return 0;
}
2.泛型编程

关于泛型编程我还没有了解太多,会在以后的学习中了解并总结。

动态多态
动态绑定:在程序执行期间(非编译期)判断所引用对象的实际类型,根据其实际类型调用相应的方法。

使用virtual关键字修饰类的成员函数时,指明该函数为虚函数,派生类需要重新实现,编译器将实现动态绑定。
#include <iostream>
#include <Windows.h>

using namespace std;

class WashRoom
{
public:
    void GoToManWashRoom()
    {
        cout << "Man-->Please Left" << endl;
    }
    void GoToWomanWashRoom()
    {
        cout << "Woman-->Please Right" << endl;
    }
};
class Person
{
public:
    virtual void GoToWashRoom(WashRoom & washRoom) = 0;
};
class Man :public Person
{
public:
    virtual void GoToWashRoom(WashRoom & washRoom)
    {
        washRoom.GoToManWashRoom();
    }
};
class Woman :public Person
{
public:
    virtual void GoToWashRoom(WashRoom & washRoom)
    {
        washRoom.GoToWomanWashRoom();
    }
};
void FunTest()
{
    WashRoom washRoom;
    for (int iIdx = 1; iIdx <= 10; ++iIdx)
    {
        Person* pPerson;
        int iPerson = rand() % iIdx;
        if (iPerson & 0x01)
            pPerson = new Man;
        else
            pPerson = new Woman;
        pPerson->GoToWashRoom(washRoom);
        delete pPerson;
        pPerson = NULL;
        Sleep(1000);
    }
}
int main()
{
    FunTest();
    system("pause");
    return 0;
}

但是想要实现多态绑定必须满足几点条件:

1.通过基类类型的引用或者指针调用虚函数

对象的类型

静态类型:对象声明时的类型,在编译期间确定。

动态类型:目前所指对象的类型,在运行期间确定。
class CBase
{};
class CDe1:public CBase
{};

class CDe2:public CBase
{};



int main()
{
    //pd1的静态类型是CDe1*,动态类型是CDe1
    CDe1* pd1 = new CDe1;
    //pb的静态类型是CBase*,动态类型是CDe1
    CBase* pb = pd1;
    CDe2* pd2 = new CDe2;
    //pb的动态类型现在是CDe2
    pb = pd2;
    system("pause");
    return 0;
}

2.必须是虚函数并且派生类一定要对虚函数进行重写

class Base
{
public :
   virtual void FunTest1( int _iTest)
   {
      cout <<"Base::FunTest1()" << endl;
   }
   void FunTest2(int _iTest)
   {
      cout <<"Base::FunTest2()" << endl;
   }
   virtual void FunTest3(int _iTest1)
   {
      cout <<"Base::FunTest3()" << endl;
   }
   virtual void FunTest4( int _iTest)
   {
      cout <<"Base::FunTest4()" << endl;
    }
};

class Derived :public Base
{
public :
   //正确
   virtual void FunTest1(int _iTest)
   {
      cout <<"Derived::FunTest1()" << endl;
   }
   //错误,基类中不是虚函数,
   virtual void FunTest2(int _iTest)
   {
      cout <<"Derived::FunTest2()" << endl;
   }
   //错误,不是虚函数
   void FunTest3(int _iTest1)
   {
   cout <<"Derived::FunTest3()" << endl;

   }
   //错误,参数列表不一样
   virtual void FunTest4(int _iTest1,int _iTest2)
   {
      cout<<"Derived::FunTest4()"<<endl;
   }
};

在静态多态的实现中有一个重载,在动态多态中有一个重写,我们也就对继承体系中同名成员函数的关系来个小的总结

  1. 重载

     1.在同一作用域中
    

    2.函数名字要相同,但是参数列表不同
    3.返回值可以不同

  2. 重写

      1.在同一作用域中
    

    2.函数名字,参数列表和返回值都相同,但是有个特例(协变)
    3.必须有virtual关键字
    4.访问限定符可以不同

  3. 重定义

    1.在不同作用域中
    2.函数名字相同
    3.在基类和派生类中,只要不构成重写的就是重定义。

纯虚函数
 在成员函数(必须为虚函数)的形参列表后面写上=0,则成员函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),
 抽象类不能实例化出对象。纯虚函数在派生类中重新定义以后,派生类才能实例化出对象。

在一开始我们定义的washroom类中就有纯虚函数

class Person
{
public:
    //纯虚函数
    virtual void GoToWashRoom(WashRoom & washRoom) = 0;
};

剖析虚函数列表

#include <iostream>

using namespace std;

class C
{
public:
    C()
    {
        i = 10;
        cout << "this = " << this << endl;
    }
    virtual ~C(){};
private:
    int i;
};

int main()
{
    C c;
    cout << sizeof(c) << endl;
    system("pause");
    return 0;
}

如果在类中我们定义的不是虚拟析构函数,是普通析构函数的话,类的大小应该为4,就是类C的成员变量i的大小,

这里写图片描述

从图中我们可以看到,此类的大小为8,
在类成员变量的上面多出了4个字节的内容,这和我们之前解决菱形问题二义性引入虚拟继承的类似

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

CTest类的内存布局:

这里写图片描述

这里写图片描述

内存2中的00 00 00 00为虚表的结束标志

单继承下的虚表列表
class Base
{
public:
virtual void FunTest0(){cout<<"Base::FunTest0()";}
virtual void FunTest1(){cout<<"Base::FunTest1()";}
virtual void FunTest2(){cout<<"Base::FunTest2()";}
};
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 (*VirtualFunPtr)();
void PrintVirtualTable(Base& b, const string& strInfo)
{
cout<< strInfo<<endl;
VirtualFunPtr* pVirtualFun = (VirtualFunPtr*)((*( int *)&b));
while(*pVirtualFun)
{
(*pVirtualFun)();
cout<< ": "<<pVirtualFun<<endl;
pVirtualFun++;
}
cout<<endl;
}
int main()
{
Base b;
Derived d;
PrintVirtualTable(b, "Base Vpf:");
PrintVirtualTable(d, "Derived Vpf:");
return 0;
}

这里写图片描述
基类内存布局
这里写图片描述
基类的虚表
这里写图片描述

派生类的内存布局
这里写图片描述

派生类的虚表
这里写图片描述

由图可知,虚函数是按照其声明顺序存在于虚表中的
在派生类中,前面是基类的虚函数,后面是派生类的虚函数。

这是在没有发生重写的情况下。

下面就来看看重写的情况

lass Base
{
public:
    virtual void FunTest0(){ cout << "Base::FunTest0()"; }
    virtual void FunTest1(){ cout << "Base::FunTest1()"; }
    virtual void FunTest2(){ cout << "Base::FunTest2()"; }
};
class Derived :public Base
{
public:
    virtual void FunTest4(){ cout << "Derived::FunTest4()"; }
    virtual void FunTest1(){ cout << "Derived::FunTest1()"; }
    virtual void FunTest6(){ cout << "Derived::FunTest6()"; }
};
typedef void(*VirtualFunPtr)();
void PrintVirtualTable(Base& b, const string& strInfo)
{
    VirtualFunPtr* pVirtualFun = (VirtualFunPtr*)((*(int *)&b));
    while (*pVirtualFun)
    {
        (*pVirtualFun)();
        cout << ": " << pVirtualFun << endl;
        pVirtualFun++;
    }
    cout << endl;
}
int main()
{
    Base b;
    Derived d;
    PrintVirtualTable(b, "Base Vpf:");
    PrintVirtualTable(d, "Derived Vpf:");
    system("pause");
    return 0;
}

这里写图片描述

多继承的情况

using namespace std;

class Base1
{
public:
    Base1(){ _b1 = 1; }
    virtual void PrintBase1(){ cout << "Base1::PrintBase1()"; }
    int _b1;
};
class Base2
{
public:
    Base2(){ _b2 = 2; }
    virtual void PrintBase2(){ cout << "Base2::PrintBase2()"; }
    int _b2;
};
class Derived :public Base1, public Base2
{
public:
    Derived(){ _d = 3; }
    virtual void PrintDdrived(){ cout << "Derived::PrintDerived()"; }
    int _d;
};
typedef void(*VirtualFunPtr)();
void PrintVt(Base1& b, const string& strInfo)
{
    VirtualFunPtr* pVirtualFun = (VirtualFunPtr*)((*(int *)&b));
    while (*pVirtualFun)
    {
        (*pVirtualFun)();
        cout << ": " << pVirtualFun << endl;
        pVirtualFun++;
    }
    cout << endl;
}
int main()
{
    Base1 b1;
    Base2 b2;
    Derived d;
    PrintVt(b1, "Base Vpf:");
    PrintVt(d, "Derived Vpf:");
    return 0;
}

其内存布局

这里写图片描述

这里写图片描述

从图中我i们可以看到,派生类的虚表是接在第一个继承的基类的后面的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值