【C++】—多态(上)动态多态

一、多态
1、 概念:同一事物表现出的多种形态,同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。
2、 举例子:

#include<windows.h>
class WashRoom
{
public:
    void GoToManWashRoom()
    {
        cout << "Man ---> Please Left" << endl;
    }
    void GoToWomanWashRoom()
    {
        cout << "Woman ------> Please Right" << endl;
    }
};

class Person
{
public:
    virtual void GoToWashRoom(WashRoom& wc) = 0;
};

class Man :public Person
{
public:
    virtual void GoToWashRoom(WashRoom& wc)
    {
        wc.GoToManWashRoom();
    }
};

class Woman :public Person
{
public:
    virtual void GoToWashRoom(WashRoom& wc)
    {
        wc.GoToWomanWashRoom();
    }
};

void TestWashRoom()
{
    WashRoom wc;
    Person* p = NULL;
    for (int i = 0; i <10; ++i)
    {
        if ((rand()) & 0x01)
            p = new Man;
        else
            p = new Woman;
        p->GoToWashRoom(wc);
        delete p;
        Sleep(1000);
    }
}

int main()
{
    TestWashRoom();
    return 0;
}

3、 多态的分类
(1)静态多态:编译器在编译期间来确定程序的行为(确定具体调用哪个函数)

A:函数重载

B:泛型编程

(2)动态多态:程序运行时来确定程序的行为(确定具体调用哪个函数)
4、 动态多态实现条件
(1) 基类中必须包含虚函数,在派生类中必须对基类的虚函数进行重写
(2) 必须通过基类指针或引用调用虚函数

//1、基类中必须包含虚函数,且派生类一定要对基类中的虚函数进行重写(原型一致)
class Base
{
public:
    virtual void TestFunc1()
    {
        cout << "Base::TestFunc1()" << endl;
    }
    virtual void TestFunc2()
    {
        cout << "Base::TestFunc2()" << endl;
    }
    void TestFunc3()//基类无虚拟关键字,派生类有
    {
        cout << "Base::TestFunc3()" << endl;
    }
    virtual void TestFunc4(int)//派生类中是char,参数类型不一样
    {
        cout << "Base::TestFunc4()" << endl;
    }
    /*virtual int TestFunc5()
    {
        cout << "Base::TestFunc5()" << endl;
        return 0;
    }*/
    //协变—基类(派生类)虚函数返回基类(派生类)指针或引用
    virtual Base* TestFunc5()
    {
        cout << "Base::TestFunc5()" << endl;
        return this;
    }
    virtual Base& TestFunc6()
    {
        cout << "Base::TestFunc6()" << endl;
        return *this;
    }

    ~Base()//析构函数
    {
        cout << "Base::~Base()" << endl;
    }
};

class Derived : public Base
{
public:
    virtual void TestFunc1()
    {
        cout << "Derived::TestFunc1()" << endl;
    }
    void TestFunc2()//派生类无虚拟关键字,基类有
    {
        cout << "Derived::TestFunc2()" << endl;
    }
    virtual void TestFunc3()
    {
        cout << "Derived::TestFunc3()" << endl;
    }
    //没有构成重写(参数类型不同)
    virtual void TestFunc4(char)//基类中是int 参数类型不同
    {
        cout << "Derived::TestFunc4()" << endl;
    }
    //virtual char TestFunc5()//不能重写,返回类型不同
    //{
    //  cout << "Derived::TestFunc5()" << endl;
    //  return 0;
    //}
    //协变—基类(派生类)虚函数返回基类(派生类)指针或引用
    virtual Derived* TestFunc5()
    {
        cout << "Derived::TestFunc5()" << endl;
        return this;
    }
    virtual Derived& TestFunc6()
    {
        cout << "Derived::TestFunc6()" << endl;
        return *this;
    }

    virtual ~Derived()
    {
        cout << "Derived::~Derived()" << endl;
    }
};

//2、通过基类对象的指针或引用调用虚函数
void TestVirtualFunc(Base& b)
{
    b.TestFunc1();
    b.TestFunc2();
    b.TestFunc3();
    b.TestFunc4(10);
    b.TestFunc4('a');
}

int main()
{
    Base* pb = new Derived;//基类的指针,指向派生类的对象
    delete pb;//如果调用基类里的析构,则没有重写;反之

    Base b;
    Derived d;
    //如果引用的是基类的对象,调用基类里对应的虚函数
    //如果引用的是派生类的对象,则调用派生类里对应的虚函数
    TestVirtualFunc(b);
    TestVirtualFunc(d);
    return 0;
}

返回值不同不能重写

5、 重写
(1)基类中的函数必须为虚函数
(2)派生类中重写的虚函数必须与基类的虚函数类型保持一致(返回值、函数名字(参数列表))
例外:

协变:基类中虚函数返回基类对象的指针或引用,派生类中虚函数返回派生类对象的指针或引用——返回值类型不同
析构函数——基类和派生类中函数的名字不同

(3)基类中虚函数和派生类虚函数的访问限定符可以不同

——重载、重写、重定义

6、代码实现多态

class Base
{
public:
    virtual void TestFunc1()
    {
        cout << "Base::TestFunc1()" << endl;
    }
    virtual void TestFunc2()
    {
        cout << "Base::TestFunc2()" << endl;
    }
    virtual void TestFunc3()
    {
        cout << "Base::TestFunc3()" << endl;
    }

    int _b;
};

class Derived :public Base
{
public:
    virtual void TestFunc2()
    {
        cout << "Derived::TestFunc2()" << endl;
    }
    virtual void TestFunc3()
    {
        cout << "Derived::TestFunc3()" << endl;
    }


    int _d;
};

      void TestVirtualFunc(Base& b)
{
b. TestFunc1();
b. TestFunc2();
}
int main()
{
   Base b;
   Derived d;

   TestVirtualFunc(b);
   TestVirtualFunc(d);
   return 0;
}

7、动态多态调用原理

多态的条件已经完全满足
1)从对象的前4个字节中取虚表地址
2) 传参(虚函数形参+this)
3) 从虚表中取虚函数地址
4) 调用该虚函数

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

class WC;

class Person
{
public:
    virtual void GoToWC(WC& c) = 0;//纯虚函数
};

class Man :public Person
{
public:
    virtual void GoToWC(WC& c)
    {
        //
    }
};
int main()
{
    Person* p;
    Man m;
    return 0;
}

2、总结
(1)派生类重写基类的虚函数实现多态,要求函数名、参数列表、返回值完全相同(协变除外)
(2)基类中定义了虚函数,在派生类中该函数始终保持虚函数的特性
(3)只有类的非静态成员函数才能定义为虚函数,静态成员函数不能定义为虚函数
(4)如果在类外定义虚函数,只能在声明函数时加virtual关键字,定义时不用加
(5)构造函数不能定义为虚函数,虽然可以将operator=定义为虚函数,但最好不要这么做,使用时容易混淆(?)
(6)不要在构造函数和析构函数中调用虚函数,在构造函数和析构函数中,对象是不完整的,可能会出现未定义的行为
(7)最好将基类的析构函数声明为虚函数。(析构函数比较特殊,因为派生类的析构函数跟基类的析构函数名称不一样,但是构成覆盖,这里编译器做了特殊处理)
(8)虚表是所有类对象实例共用的

class Base
{
public:
    virtual void TestFunc1()
    {
        cout << "Base::TestFunc1()" << endl;
    }

    int _b;
};

int main()
{
    cout << sizeof(Base) << endl;//8
    Base b;
    b._b = 1;
    return 0;
}

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值