C++中多态特性深入探究---虚函数

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/z_dmsd/article/details/77602610

首先,多态是什么?

  • 摘抄自 多态(维基百科)
    —— 多态(英语:polymorphism),是指计算机程序运行时,相同的消息可能会送给多个不同的类别之对象,而系统可依据对象所属类别,引发对应类别的方法,而有不同的行为。简单来说,所谓多态意指相同的消息给予不同的对象会引发不同的动作称之。

  • cplusclpus中有这么一句话:

One of the key features of class inheritance is that a pointer to a derived class is type-compatible with a pointer to its base class. Polymorphism is the art of taking advantage of this simple but powerful and versatile feature.

即,在C++中指向子类的指针和指向基类的指针是兼容的。

  • 所以,在C++中,我们可以通过指针指向不同的对象来实现多态(引用和指针类似,虽然有些差别,但是这里不展开讨论)

    虚函数机制

  • 参考《Effective C++》条款07中对虚表指针以及虚函数表的讲解,这里阐述下虚函数机制的原理:

    —- 在C++中,若在类中定义了虚函数,则编译器会为改类生成一个虚函数表,这个表中保存了该类以及父类所有的虚函数,并在实例化该类时,会在类对象中添加一个虚表指针,这个指针就只向该虚表指针,当使用指针或引用调用函数时,C++会根据对象中保存的虚表指针查找需要调用的函数,具体的细节下面慢慢分析。

 class Fa
{
    int Fa_a;
public:
    void foo()
    {
        cout << "父类的foo函数 \n";
    }
};

class childA:public Fa
{
    int CA_a;
public:
     void foo()
    {
        cout << "子类A的foo函数 \n";
    }
};

VS中开启类内存布局查看后的图片

  • 在没有定义虚函数时,内存布局中只存在父类和子类的数据成员对象
class Fa
{
    int Fa_a;
public:
    virtual void foo()
    {
        cout << "父类的foo函数 \n";
    }
};

class childA:public Fa
{
    int CA_a;
public:
     void foo()
    {
        cout << "子类A的foo函数 \n";
    }
};

添加了虚函数后的内存布局

  • 在添加了虚函数后,内存布局中增加了一个vfptr,并且生成了一个vftable,分别是虚表指针和虚函数表。
class Fa
{
    int Fa_a;
public:
    virtual void foo()
    {
        cout << "父类的foo函数 \n";
    }
};



class childA:public Fa
{
    int CA_a;
};

只在父类中定义foo函数

class Fa
{
    int Fa_a;
};



class childA:public Fa
{
    int CA_a;
public:
     virtual void foo()
    {
        cout << "子类A的foo函数 \n";
    }
};

只在子类定义foo函数

在这两个例子中需要注意两点:

  1. 当在父类中先定义虚函数时,vfptr出现在父类下面,当子类先定义虚函数时,vfptr出现在子类下面
  2. 当在子类中定义了和父类中虚函数相同的函数时,虚函数表中保存的是子类的虚函数,当子类中没有定义与父类虚函数相同的函数时,虚函数表中保存的是父类的虚函数。

—- 这里不难得出一下两点结论:

  1. 子类会继承父类的虚函数表,并且当子类定义了与父类相同的函数(返回值,函数签名都相同)时,子类的虚函数会覆盖父类的虚函数保存到虚函数表中。
  2. 当子类出现父类中没有的虚函数时,子类的虚函数会被添加到虚函数表中,并且偏移量大于任意父类的虚函数。

— 上面的例子还不是很清晰的说明了第二点,下面在放一个清晰一点的例子:

class Fa
{
    int Fa_a;
public:
    virtual void foo()
    {
        cout << "父类的foo函数 \n";
    }
    virtual  void FFF()
    {
        cout << "";
    }
};


class childA:public Fa
{
    int CA_a;
public:
     virtual void foo()
    {
        cout << "子类A的foo函数 \n";
    }

     virtual void funcA()
     {
         cout << "子类A的func函数\n";
     }
};

子类新增函数

  • 这个例子可以让我们清楚的理解上面两点结论,父类中先定义了虚函数,于是vfptr在Fa下面,子类定义了与父类相同的函数,于是子类的虚函数覆盖了父类的虚函数,子类新增了虚函数,于是虚函数表中添加了该虚函数,并且在父类虚函数的后面。

  • 理解了原理后我们就可以使用虚函数来实现多态了:

#include<iostream>

using namespace std;

class Fa
{
    int Fa_a;
public:
    virtual void foo()
    {
        cout << "父类的foo函数 \n";
    }
};



class childA:public Fa
{
    int CA_a;
public:
     virtual void foo()
    {
        cout << "子类A的foo函数 \n";
    }
};

class childB :public Fa
{
    int CB_a;
public:
    virtual void foo()
    {
        cout << "子类B的foo函数\n";
    }
};

void func()
{
    Fa * ff = new Fa();
    ff->foo();
    ff =new childA();
    ff->foo();
    ff = new childB();
    ff->foo();
}


int main()
{
    func();
    system("pause");
    return 0;
}

这里写图片描述

  • 可以看到,我们使用了同一个ff指针,却调用了三个不同的函数,这就是多态了。

下一篇会讲解虚函数的注意事项

猜你喜欢

转载自blog.csdn.net/z_dmsd/article/details/77602610