virtual的三种用法,引出C++类的模型

virtual用途:
1.虚成员函数
2.虚析构函数 (用于基类)//有个疑问,不如所有的析构函数都写为虚函数。
3.虚继承 (用于菱形继承的情况)

以下模型内容,部分转自:https://www.cnblogs.com/raichen/p/5744300.html
C++ 对象模型:单继承、多继承、虚继承

数据成员分为静态和非静态,成员函数有静态非静态以及虚函数
virtual的三种用法,引出C++类的模型
C++对象模型:
在此模型下,nonstatic 数据成员被置于每一个类对象中,而static数据成员被置于类对象之外。
static与nonstatic函数也都放在类对象之外,而对于virtual 函数,则通过虚函数表+虚指针来支持。

class Base
{
public:
 
    Base(int i) :baseI(i){};
  
    int getI(){ return baseI; }
 
    static void countI(){};
 
    virtual void print(void){ cout << "Base::print()"; }

    virtual ~Base(){}
 
private:
 
    int baseI;
 
    static int baseS;
};

1.每个类生成一个表格,称为虚表(virtual table,简称vtbl)。虚表中存放着一堆指针,这些指针指向该类每一个虚函数。虚表中的函数地址将按声明时的顺序排列。

2.每个类对象都拥有一个虚表指针(vptr),由编译器为其生成。虚表指针的设定与重置皆由类的复制控制(也即是构造函数、析构函数、赋值操作符)来完成。vptr的位置为编译器决定,传统上它被放在所有显示声明的成员之后,不过现在许多编译器把vptr放在一个类对象的最前端(也就是说对象的地址就是vptr的地址)。

3.虚函数表的前面设置了一个指向type_info的指针,用以支持RTTI(Run Time Type Identification,运行时类型识别)。RTTI是为多态而生成的信息,包括对象继承关系,对象本身的描述等,只有具有虚函数的对象在会生成。virtual的三种用法,引出C++类的模型

单继承(父类含虚函数)
对普通单继承而言:
子类与父类拥有各自的一个虚函数表
若子类并无overwrite父类虚函数,用父类虚函数
若子类重写(overwrite)了父类的虚函数,则子类虚函数将覆盖虚表中对应的父类虚函数
若子声明了自己新的虚函数,则该虚函数地址将扩充到虚函数表最后

#include <iostream>
using namespace std;

class Base
{
public:
    virtual void fun1(){ cout << "Base fun1" << endl; }
    virtual void fun2(){ cout << "Base fun2" << endl; }
private:
    int a;
};

class Derive :  public Base
{
public:
    void fun2(){ cout << "Derive fun2" << endl; }
    virtual void fun3(){}
private:
    int b;
};

int main()
{
    Base b;
    Derive d;
    Base *p = &d;
    p->fun1();   // Base fun1
    p->fun2();   //Derive fun2

    system("pause");
    return 0;
}

一般多继承(不考虑菱形继承)
若子类新增虚函数,放在声明的第一个父类的虚函数表中
若子类重写了父类的虚函数,所有父类的虚函数表都要改变:如fun1
内存布局中,父类按照其声明顺序排列

#include <iostream>
using namespace std;

class Base1
{
public:
    virtual void fun1(){}
    virtual void fun2(){}
	virtual void fun3(){}
	virtual void fun4(){}
	virtual void fun5(){}
	virtual void fun6(){}
private:
};

class Base2
{
public:
    virtual void fun1(){}
    virtual void fun2(){}
private:
    int m_base2;
};

class Derive :  public Base1,public Base2
{
public:
    void fun1(){}
    virtual void fun3(){}
private:
    int m_derive;
};

int main()
{
    Base1 b1;
    Base2 b2;
    Derive d;

    cout <<"b1:" <<sizeof(b1) << endl;  // 4
    cout << "b2:" << sizeof(b2) << endl; // 8
    cout <<"d:" << sizeof(d) << endl;    //16
    system("pause");
    return 0;
}

虚继承
虚继承解决了菱形继承中最派生类拥有多个间接父类实例的情况。
1.虚继承的子类,如果本身定义了新的虚函数,则编译器为其生成一个新的虚函数指针(vptr)以及一张虚函数表。该vptr位于对象内存最前面(对比非虚继承:直接扩展父类虚函数表)
2.虚继承的子类也单独保留了父类的vprt与虚函数表
3.虚继承的子类有虚基类表指针(vbptr)
virtual的三种用法,引出C++类的模型