【C++】——多态(下)再探虚表&不同继承下带有虚函数的对象模型

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

一、虚函数

1、概念:简单地说,那些被virtual关键字修饰的成员函数,就是虚函数。虚函数的作用,用专业术语来解释就是实现多态。
2、代码示例:

class Base
{
public:
    virtual void TestFunc1()
    {
        cout << "Base::TestFunc1()" << endl;
    }
    virtual void TestFunc2()
    {
        cout << "Base::TestFunc2()" << endl;
    }
    virtual void TestFunc3()
    {
        cout << "Base::TestFunc3()" << endl;
    }

    int _b;
};

typedef void(*PVTF)();
void PrintVTF(Base& b)//打印虚函数
{
    PVTF* pVTF = (PVTF*)(*(int*)&b);
    while (*pVTF)//走到最后一个位置,解引用就是一个0
    {
        (*pVTF)();//调这个函数
        ++pVTF;
    }
    cout << endl;
}
int main()
{
    cout << sizeof(Base) << endl;//8
    Base b;
    b._b = 1;

    PrintVTF(b);
    return 0;
}

上面打印虚函数的函数里面循环条件——走到最后一个位置,解引用就是一个0

分析:一个类中的虚函数
(1)如果一个类里面有虚函数,其类大小会多4个字节
其大小为:成员大小总和+4字节
注意:4字节是多了一个指向虚表的指针(虚函数的地址即虚函数的表格,简称虚表)
(2)以下结果可以说明:虚函数表格里面的虚函数排列次序就是它们在类里面声明的先后顺序

此结果中类的大小为8——成员大小4+4(有虚函数)=8

3、定义虚函数的限制:

(1)非类的成员函数不能定义为虚函数。

(2)类的成员函数中静态成员函数和构造函数也不能定义为虚函数,但可以将析构函数定义为虚函数。

(3)常常把基类的析构函数定义为虚函数。因为,将基类的析构函数定义为虚函数后,当利用delete删除一个指向派生类定义的对象指针时,系统会调用相应的类的析构函数。而不将析构函数定义为虚函数时,只调用基类的析构函数。

(4)只需要在声明函数的类体中使用关键字“virtual”将函数声明为虚函数,而定义函数时不需要使用关键字“virtual”。

(5)当将基类中的某一成员函数声明为虚函数后,派生类中的同名函数(函数名相同、参数列表完全一致、返回值类型相关)自动成为虚函数。

(6)如果声明了某个成员函数为虚函数,则在该类中不能出现和这个成员函数同名并且返回值、参数个数、类型都相同的非虚函数。在以该类为基类的派生类中,也不能出现和这个成员函数同名并且返回值、参数个数、类型都相同的非虚函数。

二、不同继承下带有虚函数的对象模型

1、带有虚函数的单继承

class Base
{
public:
    virtual void TestFunc1()
    {
        cout << "Base::TestFunc1()" << endl;
    }
    virtual void TestFunc2()
    {
        cout << "Base::TestFunc2()" << endl;
    }
    virtual void TestFunc3()
    {
        cout << "Base::TestFunc3()" << endl;
    }

    int _b;
};

class Derived :public Base
{
public:
    int _d;
};

typedef void(*PVTF)();
void PrintVTF(Base& b,const string& str)//打印虚函数
{
    PVTF* pVTF = (PVTF*)(*(int*)&b);
    cout <<str << endl;
    while (*pVTF)//走到最后一个位置,解引用就是一个0
    {
        (*pVTF)();//调这个函数
        ++pVTF;
    }
    cout << endl;
}
int main()
{
    cout << sizeof(Base) << endl;//8
    cout << sizeof(Derived) << endl;//12

    Base b;
    b._b = 1;

    Derived d;
    d._b = 1;
    d._d = 2;

    PrintVTF(b,"Base VTF:");
    PrintVTF(d, "Derived VTF:");
    return 0;
}

结果:
此时派生类中没有虚函数的打印结果

派生类的对象模型

——在派生类中加上一个虚函数(重写了之后,换成派生类自己的虚函数)

class Derived :public Base
{
public:
    virtual void TestFunc2() //重写Base类中的TestFunc2()
    {
        cout << "Derived::TestFunc2()" << endl;
    }

    int _d;
};

经过派生类的重写,打印出来以后是派生类自己的TestFunc2()

总结:

(1)基类中的虚表:将类中的虚函数按照在类中声明的先后次序添加虚函数表中
(2)派生类中的虚表:

——将基类虚函数表中的内容拷贝一份(从第一个打印即如果中可以看出)。
——如果派生类重写了基类的某个虚函数,使用派生类自己的虚函数替换派生类虚表中相同偏移量位置的基类的虚函数(从第二个打印即如果中可以看出)。按照派生类自己特有的虚函数的声明次序将其增加到虚函数表的最后。

2、带有虚函数的多继承

class B1
{
public:
    virtual void TestFunc1()
    {
        cout << "B1::TestFunc1()" << endl;
    }
    virtual void TestFunc2()
    {
        cout << "B1::TestFunc2()" << endl;
    }
public:
    int _b1;
};

class B2
{
public:
    virtual void TestFunc3()
    {
        cout << "B2::TestFunc3()" << endl;
    }
    virtual void TestFunc4()
    {
        cout << "B2::TestFunc4()" << endl;
    }
public:
    int _b2;
};

class D : public B1, public B2
{
public:
    virtual void TestFunc2()
    {
        cout << "D::TestFunc2()" << endl;
    }
    virtual void TestFunc3()
    {
        cout << "D::TestFunc3()" << endl;
    }
public:
    int _d;
};

#include<string>
typedef void(*PVTF)();
void PrintVTF(B1& b, const string& str)//打印虚函数
{
    PVTF* pVTF = (PVTF*)(*(int*)&b);
    cout << str << endl;
    while (*pVTF)//走到最后一个位置,解引用就是一个0
    {
        (*pVTF)();//调这个函数
        ++pVTF;
    }
    cout << endl;
}
void PrintVTF(B2& b, const string& str)//打印虚函数
{
    PVTF* pVTF = (PVTF*)(*(int*)&b);
    cout << str << endl;
    while (*pVTF)//走到最后一个位置,解引用就是一个0
    {
        (*pVTF)();//调这个函数
        ++pVTF;
    }
    cout << endl;
}
int main()
{
    cout << sizeof(D) << endl;

    D d;
    d._b1 = 1;
    d._b2 = 2;
    d._d = 3;

    B1& b1 = d;
    PrintVTF(b1, "D VFT— > B1");
    B2& b2 = d;
    PrintVTF(b2, "D VFT— > B2");
    return 0;
}

派生类对象模型
将派生类新增加的虚函数放置到第一张虚表的最后。

3、带有虚函数的菱形继承(存在二义性)

class B
{
public:
    virtual void TestFunc1()
    {
        cout << "B::TestFunc1()" << endl;
    }
    virtual void TestFunc2()
    {
        cout << "B::TestFunc2()" << endl;
    }
public:
    int _b;
};

class C1 :public B
{
public:
    virtual void TestFunc1()//重写B里面的TestFunc1()
    {
        cout << "C1::TestFunc1()" << endl;
    }
    virtual void TestFunc3()//自己新增一个TestFunc3()
    {
        cout << "C1::TestFunc3()" << endl;
    }

    int _c1;
};

class C2 :public B
{
public:
    virtual void TestFunc2()//重写B里面的TestFunc2()
    {
        cout << "C2::TestFunc2()" << endl;
    }
    virtual void TestFunc4()//自己新增一个TestFunc4()
    {
        cout << "C2::TestFunc4()" << endl;
    }

    int _c2;
};

class D :public C1, public C2
{
public:
    virtual void TestFunc1()//重写
    {
        cout << "D::TestFunc1()" << endl;
    }
    virtual void TestFunc3()//重写
    {
        cout << "D::TestFunc3()" << endl;
    }
    virtual void TestFunc4()//重写
    {
        cout << "D::TestFunc4()" << endl;
    }
    virtual void TestFunc5()//自己新增的
    {
        cout << "D::TestFunc5()" << endl;
    }

    int _d;
};

#include<string>
typedef void(*PVTF)();

void PrintVTF(C1& b, const string& str)//打印虚函数
{
    PVTF* pVTF = (PVTF*)(*(int*)&b);
    cout << str << endl;
    while (*pVTF)//走到最后一个位置,解引用就是一个0
    {
        (*pVTF)();//调这个函数
        ++pVTF;
    }
    cout << endl;
}
void PrintVTF(C2& b, const string& str)//打印虚函数
{
    PVTF* pVTF = (PVTF*)(*(int*)&b);
    cout << str << endl;
    while (*pVTF)//走到最后一个位置,解引用就是一个0
    {
        (*pVTF)();//调这个函数
        ++pVTF;
    }
    cout << endl;
}
int main()
{
    cout << sizeof(D) << endl;//28
    D d;
    d.C1::_b = 1;
    d._c1 = 2;

    d.C2::_b = 3;
    d._c2 = 4;
    d._d = 5;

    C1& c1 = d;
    PrintVTF(c1, "D VFT—>C1:");
    C2& c2 = d;
    PrintVTF(c2, "D VFT—>C2:");
    return 0;
}

派生类对象模型

4、带有虚函数的虚拟继承

class B
{
public:
    virtual void TestFunc1()
    {
        cout << "B::TestFunc1()" << endl;
    }
    virtual void TestFunc2()
    {
        cout << "B::TestFunc2()" << endl;
    }
public:
    int _b;
};

class D :virtual public B
{
public:
    virtual void TestFunc2()//重写
    {
        cout << "D::TestFunc2()" << endl;
    }
    virtual void TestFunc3()//自己新增的
    {
        cout << "D::TestFunc3()" << endl;
    }

    int _d;
};

#include<string>
typedef void(*PVTF)();

void PrintVTF(B& b, const string& str)//打印虚函数
{
    PVTF* pVTF = (PVTF*)(*(int*)&b);
    cout << str << endl;
    while (*pVTF)//走到最后一个位置,解引用就是一个0
    {
        (*pVTF)();//调这个函数
        ++pVTF;
    }
    cout << endl;
}

int main()
{
    cout << sizeof(D) << endl;//16

    int a = -4;
    D d;
    d._b = 1;
    d._d = 2;

    B& b = d;
    PrintVTF(b, "D—>B:");
    PrintVTF(d, "D—>D:");
    return 0;
}

派生类对象的模型

(1)派生类未增加新的虚函数

(2)派生类增加了新的虚函数

5、带有虚函数的菱形虚拟继承

class B
{
public:
    virtual void TestFunc1()
    {
        cout << "B::TestFunc1()" << endl;
    }
    virtual void TestFunc2()
    {
        cout << "B::TestFunc2()" << endl;
    }
    int _b;
};

class C1 :virtual public B
{
public:
    virtual void TestFunc1()//重写B里面的TestFunc1()
    {
        cout << "C1::TestFunc1()" << endl;
    }
    virtual void TestFunc3()//自己新增一个TestFunc3()
    {
        cout << "C1::TestFunc3()" << endl;
    }

    int _c1;
};
class C2 :virtual public B
{
public:
    virtual void TestFunc2()//重写B里面的TestFunc2()
    {
        cout << "C2::TestFunc2()" << endl;
    }
    virtual void TestFunc4()//自己新增一个TestFunc4()
    {
        cout << "C2::TestFunc4()" << endl;
    }

    int _c2;
};

class D :public C1, public C2
{
public:
    virtual void TestFunc1()//重写
    {
        cout << "D::TestFunc1()" << endl;
    }
    virtual void TestFunc3()//重写
    {
        cout << "D::TestFunc3()" << endl;
    }
    virtual void TestFunc4()//重写
    {
        cout << "D::TestFunc4()" << endl;
    }
    virtual void TestFunc5()//新增加的
    {
        cout << "D::TestFunc5()" << endl;
    }

    int _d;
};

#include<string>
typedef void(*PVTF)();
void PrintVTF(B& b, const string& str)//打印虚函数
{
    PVTF* pVTF = (PVTF*)(*(int*)&b);
    cout << str << endl;
    while (*pVTF)//走到最后一个位置,解引用就是一个0
    {
        (*pVTF)();//调这个函数
        ++pVTF;
    }
    cout << endl;
}
void PrintVTF(C1& b, const string& str)//打印虚函数
{
    PVTF* pVTF = (PVTF*)(*(int*)&b);
    cout << str << endl;
    while (*pVTF)//走到最后一个位置,解引用就是一个0
    {
        (*pVTF)();//调这个函数
        ++pVTF;
    }
    cout << endl;
}
void PrintVTF(C2& b, const string& str)//打印虚函数
{
    PVTF* pVTF = (PVTF*)(*(int*)&b);
    cout << str << endl;
    while (*pVTF)//走到最后一个位置,解引用就是一个0
    {
        (*pVTF)();//调这个函数
        ++pVTF;
    }
    cout << endl;
}

int main()
{
    cout << sizeof(D) << endl;//36
    D d;
    d._b = 1;
    d._c1 = 2;
    d._c2 = 3;
    d._d = 4;

    B& b = d;
    PrintVTF(b, "D VFT—>B:");
    C1& c1 = d;
    PrintVTF(c1, "D VFT—>C1:");
    C2& c2 = d;
    PrintVTF(c2, "D VFT—>C2:");
    return 0;
}

派生类的对象模型
(1)移量表格

(2)虚表

经过代码验证,结果正确

附加:在C1、C2和D类里加入构造函数或者析构函数,则派生类大小多了4字节

猜你喜欢

转载自blog.csdn.net/Paranoid_cc/article/details/81217313
今日推荐