C++ - 多态

目录

一、定义及实现

1.1 多态条件

1.2 虚函数

1.3 虚函数重写

1.3.1协变

1.3.2 析构函数重写

1.4 final

1.5 override

二、抽象类

2.1 纯虚函数

三、多态原理

 四、单继承和多继承中的虚表


一、定义及实现

多态就是执行某项行为,不同的对象去执行会有不同的状态。之前的函数重载(静态多态)就是一种多态。

1.1 多态条件

        1. 必须通过基类的指针或者引用调用虚函数
        2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写

1.2 虚函数

        在函数前加上virtual关键字,该函数就是虚函数。虚函数只能是类成员非静态函数,内联函数不能是虚函数。

class Person
{
    virtual void printPrice()
    {
        cout<<20<<endl;
    }
};

1.3 虚函数重写

        子类中有一个成员函数与父类中的完全相同(除内部实现外,其余完全相同)。

class Person
{
public:
    virtual void printPrice()
    {
        cout << 20 << endl;
    }
};
class Children : public Person
{
public:
    virtual void printPrice()
    {
        cout << 10 << endl;
    }
};
int main()
{
    Person p;
    Children c;
    Person& t1 = p;
    Person& t2 = c;
    p.printPrice();
    c.printPrice();
    return 0;
}

                                

         但是也有两个特别的重写,分别是协变和析构函数重写。

1.3.1协变

        协变情况下,父子类中的虚函数成员返回值不同,基类虚函数返回基类对象的指针或者引
用,派生类虚函数返回派生类对象的指针或者引用。

class A {};
class B : public A {};
class Person
{
public:
    virtual A& printPrice(A& a)
    {
        cout << 20 << endl;
        return a;
    }
};
class Children : public Person
{
public:
    virtual B& printPrice(B& b)
    {
        cout << 10 << endl;
        return b;
    }
};

1.3.2 析构函数重写

        父子类的析构函数名不同,但是编译器会自动在编译时将函数名弄成一样的函数名。析构函数重载利用在delete调用时。

class Person
{
public:
    virtual ~Person()
    {
        cout << "Person 析构" << endl;
    }
};
class Children : public Person
{
public:
    virtual ~Children()
    {
        cout << "Children 析构" << endl;
    }
};
int main()
{
    Person* p1 = new Person;
    Person* p2 = new Children;
    delete p1;
    cout << endl;
    delete p2;
    return 0;
}

1.4 final

        final关键字是c++11语法,在类后面加表示类不能被继承,在虚函数后面加表示不能被重写。

class A final
{};

class B
{
public:
    void f() final {}
};

1.5 override

        检查子类虚函数是否完成重写。

class A 
{
public:
    void f() final {}

};

class B : public A
{
public:
    void f() override {}
};

二、抽象类

        含有纯虚函数的类就叫做抽象类。

2.1 纯虚函数

        纯虚函数只声明,不实现(实现没有价值)。

class A
{
    virtual void f()=0;
};

        子类继承父类后也不能实例化出对象,只有重写纯虚函数,子类才能实例化出对象。纯
虚函数规范了子类必须重写,同时也更能体现接口继承。

class A
{
public:
    virtual void f()=0;
};
class B : public A
{
public:
    virtual void f()
    {
        cout<<"B::f()"<<endl;
    }
};

三、多态原理

        调试一下以下代码,可以发现父子两类中存在一个成员叫做_vfptr的变量存放着一个地址,这个_vfptr叫做虚函数表指针,该表上存放着对应类中的虚函数的地址。

class A
{
public:
    virtual void f()
    {
        cout << "A::f()" << endl;
    }
};
class B : public A
{
public:
    virtual void f()
    {
        cout << "B::f()" << endl;
    }
};
void test(A& c)
{
    c.f();
}
int main()
{
    A a;
    B b;
    test(a);
    test(b);
	return 0;
}

        通过对应类的引用去找该对象类中的虚表,再从虚表中的虚函数地址来调用相应的虚函数,以此来实现多态。

 四、多继承中的虚表

        多继承会有多张虚表。继承几个类就有几个虚表。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class A
{
public:
    virtual void f1()
    {
        cout << "A::f1()" << endl;
    }
    virtual void f2()
    {
        cout << "A::f2()" << endl;
    }
};
class B
{
public:
    virtual void f1()
    {
        cout << "B::f1()" << endl;
    }
    virtual void f3()
    {
        cout << "B::f3()" << endl;
    }
};
class C : public A ,public B
{
public:
    virtual void f1()
    {
        cout << "C::f1()" << endl;
    }
};
typedef void(* VF_PTR )();
void printVFTable(VF_PTR* table)
{
    for (int i = 0; table[i]!=nullptr; i++)
    {
        printf("vft[%d]:%p->", i, table[i]);
        table[i]();
    }
}

int main()
{
    C c;
    printVFTable((VF_PTR*)(*(void**)&c));
    B& b = c;
    printVFTable((VF_PTR*)(*(int*)&b));
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值