C++——多态

-基于VS2017编译器环境下
C ++的三大特性:封装,继承,态多下面我们看多态的英文什么:。多态概念:多态从字面的意思就是“多种状态”,但是在C ++中,多态有着更广泛的含义。
这里写图片描述
从图中,可看出,多态分为静态多态和动态多态,静态多态中,还分有函数重载和泛型编程动态多态就是有虚函数静态多态:静态多态是编译器在编译期间完成的,编译器根据函数实参的类型(可能会进行隐式类型的转换),可以推断出要调用那个函数,如果有对应的函数,就调用该函数,如果没有就编译错误。动态多态:动态多态需要虚函数,在基类中有虚函数,在派生类中进行虚函数重写。
动态多态的条件:
1.基类中必须包含虚函数,并且派生类一定要对基类中的虚函数进行重写
2.通过基类对象的指针或者引用调用虚函数
下面通过一段简单的代码了解一下多态调用:

class Base
{
public:
    virtual void fun1()//虚函数
    {
        cout << "Base::fun1" << endl;
    }
    void fun2()//普通函数
    {
        cout << "Base::fun2" << endl;
    }
    virtual void fun3()//虚函数
    {
        cout << "Base::fun3" << endl;
    }
};
class Derived :public Base
{
public:
    virtual void fun1()//派生类中进行了fun1()重写
    {
        cout << "Derived::fun1" << endl;
    }
    virtual void fun2()//虚函数
    {
        cout << "Derived::fun2" << endl;
    }
    void fun3()//普通函数
    {
        cout << "Derived::fun3" << endl;
    }
};
void test(Base &b)    //基类的指针或者引用传参
{
    b.fun1();
    b.fun2();
    b.fun3();
}
int main()
{
    Base b;
    Derived d;
    test(b);  //参数为基类对象
    test(d);  //参数为派生类对象
    return 0;
}

这里写图片描述

重写是什么?
下面我们通过一个图来理清,继承体系中同名成员函数的关系:
这里写图片描述

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

class Base
{
public:
    virtual void fun1() = 0;
};
int main()
{
    Base b;
    return 0;
}

这里写图片描述

总结
派生类重写基类的虚函数实现多态,要求函数名,参数列表,返回值完全相同(协变)
基类中定义了虚函数,在派生类中该函数始终保持虚函数的特性
只有类的非静态成员函数才能定义为虚函数,静态成员函数不能定义成虚函数
如果在类外定义虚函数,只能在声明函数时加上virtual关键字,定义时不用加
构造函数不能定义成虚函数,虽然可以将operator=定义为虚函数,最好不要这样做(容易混)
不要在构造函数和析构函数中调用虚函数,在构造函数和析构函数中,对象是不完整的,可能会出现未定义的行为
最好将基类的析构函数声明为虚函数
虚表是所以类对象实例共用的

理解了多态,下面我们来看看多态的调用原理:
为什么派生类对基类的虚函数进行重写后,通过基类对象的指针或者引用,调用该函数时,就可以实现多态?

class Base
{
public:
    virtual void fun1()
    {
        cout << "Base::fun1()" << endl;
    }
    virtual void fun2()
    {
        cout << "Base::fun2()" << endl;
    }
    int _b;
};
int main()
{
    Base b;
    b._b = 1;
    cout << sizeof(b) << endl;
    return 0;
}

这里写图片描述
我们可以理解为,当我们定义类中有虚函数时,编译器会为对象多开辟4个字节作为对象的起始地址,对象的前4个字节存放着一个虚表,虚表的内容是类中定义的虚函数的地址,使我们调用虚函数时可以找到。


不同继承下带有虚函数的对象模型:
单继承:

class Base
{
public:
    virtual void fun1()
    {
        cout << "Base::fun1()" << endl;
    }
    virtual void fun2()
    {
        cout << "Base::fun2()" << endl;
    }
    /*
    virtual void fun3()
    {
        cout << "Base::fun3()" << endl;
    }
    */
    int _b;
};
class Derived :public Base
{
public:
    virtual void fun1()
    {
        cout << "Derived::fun1()" << endl;
    }
    virtual void fun2()
    {
        cout << "Derived::fun2()" << endl;
    }
    int _d;
};
int main()
{
    Derived d;
    d._b = 1;
    d._d = 2;
    cout << "d的大小为"<<sizeof(d) << endl;
    return 0;
}

这里写图片描述
我们可以看到,对象的前4个字节放的依旧是一个指针,指针指到的地方是一个虚表,由于派生类中的虚函数是对基类中虚函数的重写,所以可以看到虚表中放的是派生类虚函数的地址。即模型为:
这里写图片描述


多继承:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值