多态

多态的提出:
为什么会有多态:多态:从字面意思来看具有多种形式或形态的情态。我们都知道,C++的封装性是为了让代码更加的模块化,而我们之前学的继承可以在我们之前学过的代码上进行扩展,这两种的功能都是实现代码的复用,但是多态的提出是为了实现接口的重用,在C++中,我们可以让一个基类的指针指向多个子类的对象,调用相应的虚函数,调用的结果就不同。从而实现了一个接口(基类)可以实现多种功能(子类对象的不同)。

多态实现的基础:
必须是虚函数,只有在基类中定义函数为虚函数,才能实现多态性,如果没有使用虚函数的话,指向派生类的基类的指针只会调用基类的成员函数(单一性),就不会体现C++的多态性。

多态的分类:
①静态多态:函数重载:Add (之前举过这个例子) 泛在编程
编译器在编译期间完成,编译器根据函数实参的类型,可推断出要调用哪个函数,如果有对应的函数就调用这个函数,
否则就会出现编译错误。
②动态多态:必须满足两个条件:①必须是虚函数:②通过基类类型的引用或指针调用虚函数(接口的)
虚函数重写的时候必须要注意两个条件:①一定是虚函数,②函数一个在基类,一个在派生类③函数原型、返回值、(参数列表)有一个例外就是:协变

虚函数:

虚函数是重载的另一种表现形式,这是一种动态的重载方式,虚函数允许在函数调用与函数体之间的联系在运行时才建立联系,也就是说在运行时才决定如何动作,也就是所谓的动态连编。
虚函数的引入,为什么要用虚函数呢?
我们先看一个程序:

class Base
{
public:
    Base(int m)
    {
        _b = m;
    }
    void show()
    {
        cout << _b << endl;
    }
private:
    int _b;
};
class Derived:public Base//公有继承
{
public:
    Derived(int m, int n) :Base(n)
    {
        _d = m;
    }
    void show()
    {
        cout << _d << endl;
    }
private:
    int  _d;
};
void Test()
{
    Base b(10), *pa;
    Derived d(30, 40);
    pa = &b;
    pa->show();
    pa = &d;
    pa->show();
}

程序的显示结果:
这里写图片描述
本来我们预期的结果是pa = &d; pa->show();等于30.因为它指向的是派生类类的对象,但是它只是把基类的值显示了出来,原因是在C++中规定:基类的指针在指向公有派生类的对象时,只能访问派生类从基类继承而来的成员,而不能访问公有派生类中定义的成员。
使用对象指针的目的是为了表达一种动态的性质,也就是说当指针指向不同的对象时,调用的是不同类的成员函数,那么将函数设置为虚函数,就能实现这种动态调用的功能。

虚函数的作用和定义

其实在上一个程序中,我们也可以用其他的方法来实现对派生类成员的调用:
①d.show();
②pa = &d;
((Derived*)pa)->show();//用指针类型强制转化(这种方式是静态连编)
但是我们既然用对象的指针,就是为了想要达到这一个动态的效果,上面的两种方法没有起到这种作用,那么我们只要将show函数设置为虚函数,在函数前面加上virtual;那么结果就显示正确。那么为什么正确?函数在调用“pa->show()”,要在运行是确定所需要的函数,即要对该函数进行动态连编,因此,程序在运行时指针所指向的实际的对象,调用该对象的成员函数,虚函数可以使C++支持运行时的多态性。

class Base
{
public:
    Base(int m)
    {
        _b = m;
    }
    virtual void show()
    {
        cout << _b << endl;
    }
private:
    int _b;
};
class Derived:public Base
{
public:
    Derived(int m, int n) :Base(n)
    {
        _d = m;
    }
    virtual void show()
    {
        cout << _d << endl;
    }
private:
    int  _d;
};
void Test()
{
    Base b(10), *pa;
    Derived d(30, 40);
    pa = &b;
    pa->show();
    pa = &d;
    pa->show();
    }

程序可以只在基类中使用虚函数,在派生类中可以不使用,只要它的函数名称,返回值、参数列表相同就会自动的认为派生类的函数也是虚函数,基类对象所指向的对象不同,运行的结果就不同。
下面对虚函数的定义作几点说明:
①虚函数使用的基础是赋值兼容规则,而赋值兼容规则的前提条件是派生类从基类公有派生,
②必须在类中定义虚函数
③在派生类中对虚函数进行重新定义的时候,virtual可以写也可以不写
⑤一个虚函数不论被公有继承多少次。
⑥虚函数必须是类的成员函数,而不能是友元函数或者静态成员函数,因为虚函数的调用要靠特定的对象来决定该激活哪个函数
⑦内联函数不能是虚函数,构造函数不能是虚函数,析构函数本来就是虚函数

#include <iostream>
using namespace std;
class Base
{
public:
    virtual void FunTest()
    {
        cout << "Base::FunTest()" << endl;
    }
};
class Derived:public Base 
{
public:
    virtual void FunTest()
    {
        cout << "Derived::FunTest()" << endl;
    }
};
void Test(Base& b)
{
    b.FunTest();
}
int main()
{
    Base b;
    Derived d;
    Test(b);
    Test(d);
    return 0;
}

上述的代码实现了单继承中的多态:
单继承中的虚表的问题:
下面的例子是一个单继承的问题,基类的指针在实例化对象的时候就已经有了一个函数的指针,而这个函数的指针中存在这函数的地址。派生类里面也有这个虚表,用图对这个具体的进行说明

#include <iostream>
using namespace std;
class Base
{
public:
    virtual void FunTest1()//虚函数
    {

        cout << "Base::FunTest1()" << endl;
    }
    virtual void FunTest2()
    {
        cout << "Base::FunTest2()" << endl;
    }
    int _data;
};
class Derived :public Base//公有继承
{
public:
    virtual void FunTest1()//对FunTest1()进行了重写
    //重写必须满足的条件有:①一定是虚函数②一个在基类,一个在派生类③函数原型相同(函数名称、函数的参数列表、函数的返回值)(除了协变)
    {
        cout << "Derived::FunTest1()" << endl;
    }
    virtual void FunTest3()
    {
        cout << "Derived::FunTest3()" << endl;
    }
};
//打印虚表
//定义一个函数指针
typedef void(*PVTF)();
void PrintfVpt(Base& b,const char*_table)
{
    cout << _table << endl;
    PVTF* Pvtf=(PVTF *)(*(int*)(&b));
    while (*Pvtf)
    {
        (*Pvtf)();
        ++Pvtf;
    }
}
void test()
{

    Base b;
    cout << sizeof(b) << endl;
    PrintfVpt(b, "Base-->");


    Derived  d;
    cout << sizeof(d) << endl;
    Base& rb = d;
    PrintfVpt(rb, "Derived-->");
}
int main()
{

    test();
    return 0;
}

这里写图片描述
简单继承虚表的过程:
①虚函数中的次序是类中的声明次序;
②没有重写基类的虚函数之前,派生类中的虚表和基类中的是相同的,如果派生类重写了基类的虚函数,用派生类重写的虚函数替换相同偏移量位置的虚函数的地址,如果派生类定义了自己的虚函数,新的虚函数在继承于基类的虚函数的地址后面,就是生命次序;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值