瞅瞅C++中的对象模型(中)

本文深入探讨C++对象模型,特别是虚函数表的工作原理及其在一般继承、多重继承中有无虚函数覆盖情况下的行为表现,揭示多态实现机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

上一篇讲了C++对象模型中的一部分,这里接着总结。

1.4 一般继承无虚函数覆盖

这里说的情况主要是子类继承了父类的虚函数,但没有改写父类的虚函数。 继承关系图如下:
在这里插入图片描述
实验代码如下:

Base 类和前一个相同。

//Derive类:
class Derive:public Base{
public:

    Derive(int val = 0):d_data(val){
        cout<<"constructor in Derive\n";
    }

     //以下三个是测试的虚函数
    virtual void f1() { cout << "virtual Derive::f1" << endl; }
    virtual void g1() { cout << "virtual Derive::g1" << endl; }
    virtual void h1() { cout << "virtual Derive::h1" << endl; }


    void testInDerive(){
        cout<<"show data member in derive: "<<d_data<<endl;
    }

    int d_data;

};

mian函数:

#include<iostream>
using namespace std;

#define MAXSIZE 100

#include "derive1.h"

typedef void(*Fun)();

int main()
{
    Base b(100),b1(1000);

    Derive d(200);
    cout<<"the size of d(Derive):"<<sizeof(d)<<endl;

    Fun pFun = NULL;

//    cout << "virtual  pointer's  address :" << *(int*)(&d) << endl;
//    cout << "the address of first virtual function is:" << (int*)*(int*)(&d) << endl;


    cout<<"\nvirtual function test.....\n";
    cout<<"\n for b(Base)\n";

//    pFun = (Fun)*((int*)*(int*)(&b));
//    pFun();

    pFun = (Fun)*((int*)*(int*)(&b)+0);  // Base::f()
    pFun();

    pFun = (Fun)*((int*)*(int*)(&b)+1);  // Base::g()
    pFun();

    pFun = (Fun)*((int*)*(int*)(&b)+2);  // Base::h()
    pFun();

    cout<<"\n for d(derive):\n";
//    pFun = (Fun)*((int*)*(int*)(&d));
//    pFun();

    pFun = (Fun)*((int*)*(int*)(&d)+0);  // Base::f()
    pFun();

    pFun = (Fun)*((int*)*(int*)(&d)+1); // Base::g()
    pFun();

    pFun = (Fun)*((int*)*(int*)(&d)+2);  // Base::h()
    pFun();

    pFun = (Fun)*((int*)*(int*)(&d)+3);  // Derive::f1()
    pFun();

    pFun = (Fun)*((int*)*(int*)(&d)+4);  // Derive::g1()
    pFun();

    pFun = (Fun)*((int*)*(int*)(&d)+5);  // Derive::h1()
    pFun();

    cout<<"\ndata member test...\n";
    int *pdata = (int*)((int*)(&d)+1);      //虚函数加4个字节即是第一个数据成员的地址
    cout<<"b_data:"<<*pdata<<endl;

    pdata = (int*)((int*)(&d)+2);
    cout<<"d_data"<<*pdata<<endl;

    int c;
    cin>>c;
    return 0;
}

结果如下:
在这里插入图片描述
同样的,这里我想说以下几点:
(1)、对于有虚函数的继承来说,子类会共用父类的vptr槽位,也就是说子类不会再增加一个针对子类的vptr(只针对这种情况,多重继承和虚继承在此不考虑)。子类的虚函数添加在继承父类的虚函数表中。
 
(2)、子类数据成员添加在继承自父类的数据成员之后。

 
基本示意图如下:
在这里插入图片描述

 

1.5 一般继承有虚函数覆盖

前面算是有了一点多态的模子,但是还没有真正实现多态。如果要实现多态,一般来说需要在子类中改写(overwrite)父类的同名函数。 ok,那么如果子类有改写父类的虚函数,其内存模型又是怎么样的呢?

en,稍微修改一下实验代码,使其继承结构如下所示:
在这里插入图片描述
修改Derive类中的函数如下:

class Derive:public Base{
public:

    Derive(int val = 0):d_data(val){
        cout<<"constructor in Derive\n";
    }

     //以下三个是测试的虚函数
    virtual void f1() { cout << "virtual Derive::f1" << endl; }
 
    //重写父类的g函数
    virtual void g() { cout << "virtual Derive::g1" << endl; }	
 	//重写父类的h函数
    virtual void h() { cout << "virtual Derive::h1" << endl; }

    void testInDerive(){
        cout<<"show data member in derive: "<<d_data<<endl;
    }

    int d_data;

};

也要稍微改下main函数:

#include<iostream>
using namespace std;

#define MAXSIZE 100

#include "derive1.h"

typedef void(*Fun)();

int main()
{
    Base b(100),b1(1000);

    Derive d(200);
    cout<<"the size of d(Derive):"<<sizeof(d)<<endl;

    Fun pFun = NULL;

//    cout << "virtual  pointer's  address :" << *(int*)(&d) << endl;
//    cout << "the address of first virtual function is:" << (int*)*(int*)(&d) << endl;


    cout<<"\nvirtual function test.....\n";
    cout<<"\n for b(Base)\n";

//    pFun = (Fun)*((int*)*(int*)(&b));
//    pFun();

    pFun = (Fun)*((int*)*(int*)(&b)+0);  // Base::f()
    pFun();

    pFun = (Fun)*((int*)*(int*)(&b)+1);  // Base::g()
    pFun();

    pFun = (Fun)*((int*)*(int*)(&b)+2);  // Base::h()
    pFun();

    cout<<"\n for d(derive):\n";
//    pFun = (Fun)*((int*)*(int*)(&d));
//    pFun();

    pFun = (Fun)*((int*)*(int*)(&d)+0);  // Base::f()
    pFun();

    pFun = (Fun)*((int*)*(int*)(&d)+1); // Derive::g()
    pFun();

    pFun = (Fun)*((int*)*(int*)(&d)+2);  // Derive::h()
    pFun();

    pFun = (Fun)*((int*)*(int*)(&d)+3);  // Derive::f1()
    pFun();

//    pFun = (Fun)*((int*)*(int*)(&d)+4);  // Derive::g1()
//    pFun();
//
//    pFun = (Fun)*((int*)*(int*)(&d)+5);  // Derive::h1()
//    pFun();

    cout<<"\ndata member test...\n";
    int *pdata = (int*)((int*)(&d)+1);      //虚函数加4个字节即是第一个数据成员的地址
    cout<<"b_data:"<<*pdata<<endl;

    pdata = (int*)((int*)(&d)+2);
    cout<<"d_data"<<*pdata<<endl;

    return 0;
}

 
最终的实验结果如下图所示:
在这里插入图片描述
 
这个实验主要就想说一点,即子类的同名函数会在虚函数表中覆盖掉父类的虚函数地址。即像下图这样:
在这里插入图片描述

 
okok,重点来了,这和多态有什么关系?

通过,这种“悄无声息”(其实背后是编译器帮我们实现了替换)的替换,我们就可以用父类的指针或者引用指向整个继承体系中的不同对象,在实际调用的时候,虽然函数名字相同,但是实际调用的函数已经不同了。

如下图所示。
在这里插入图片描述
如果bp指向的真实对象是b,那么bp->g(),调用的就是Base 类的虚函数; 相反,如果bp指向的是d,那么bp->g(),最终调用的就是Derive 里的虚函数g()。 由此达到了同样的代码根据不同的实际对象调用不同的函数,也就是实现了多态(确切的说是多态的一种)

 
上面将的基本上是C++中的内存模型的重点和难点了。 接下来的部分算是补充,因为说实话,多重继承和虚继承在工程实践中用到的并不是非常多(就我目前了解的来说),从某种程度上来说算是个“鸡肋”, 但是出于完整性考虑,下面也就一并总结了。

1.6 多重继承无虚函数覆盖

多重继承是C++支持的特性之一,即一个子类可以有多个父类,可以继承多个父类的数据成员和成员函数(当然,虚函数也可以被继承下来)。为了支持多重继承下的多态,C++中的内存模型设计了子类可以继承父类的多个虚表指针(从某种程度上来说也就相当于继承了父类的多个虚表) 。

下面就来小探究一波。
 
 
设计一个继承类图,如下。
在这里插入图片描述
 
 
参考【2】,设计了实验代码,如下。

Base1类

class Base1 {

public:

    Base1(int val = 10):b1_data(val){ cout<<"constructor in base1\n";}      //构造函数

    //以下三个是测试的虚函数
    virtual void f() { cout << "virtual Base1::f" << endl; }
    virtual void g() { cout << "virtual Base1::g" << endl; }

    virtual void h1() { cout << "virtual Base1::h1" << endl; }

    void show(){cout<<"Base1:: show"<<endl;}     //普通的成员函数
    int b1_data;     //数据成员
};

Base2类

class Base2 {
public:
    Base2(int val = 20):b2_data(val){ cout<<"constructor in base2\n";}      //构造函数

    //以下三个是测试的虚函数
    virtual void f() { cout << "virtual Base2::f" << endl; }
    virtual void g() { cout << "virtual Base2::g" << endl; }

    virtual void h2() { cout << "virtual Base2::h2" << endl; }
    
    void show(){cout<<"Base2:: show"<<endl;}     //普通的成员函数
    int b2_data;     //数据成员

};

Base3类:

class Base3 {
public:
    Base3(int val = 30):b3_data(val){ cout<<"constructor in base3\n";}      //构造函数

    //以下三个是测试的虚函数
    virtual void f() { cout << "virtual Base3::f" << endl; }
    virtual void g() { cout << "virtual Base3::g" << endl; }
    virtual void h3() { cout << "virtual Base3::h3" << endl; }

    void show(){cout<<"Base3:: show"<<endl;}     //普通的成员函数
    int b3_data;     //数据成员
};

Derive类

class Derive:public Base1,public Base2,public Base3{
public:

    Derive(int val = 100):d_data(val){
        cout<<"constructor in derive\n";
    }

//    virtual void f(){cout << "virtual Derive::f" << endl;}      //重写子类的虚函数
//
//    virtual void h1(){cout << "virtual Derive::h1" << endl;}    //重写子类的虚函数

    virtual void i(){cout << "virtual Derive::i" << endl;}      //增添的虚函数
    int d_data;       //数据成员
};

main函数:

typedef void(*Fun)();
int main()
{
    Base1 b(100),b1(1000);

    Derive d(200);
    cout<<"the size of d(Derive):"<<sizeof(d)<<endl;

    int** pVtab = (int**)&d;
    Fun pFun = NULL;

    cout<<"\nvirtual function test.....\n";


    cout << "[0] Base1:: vptr->" << endl;

    pFun = (Fun)pVtab[0][0];
    cout << "     [0] ";
    pFun();

    pFun = (Fun)pVtab[0][1];
    cout << "     [1] ";
    pFun();

     pFun = (Fun)pVtab[0][2];
    cout << "     [2] ";
    pFun();

    pFun = (Fun)pVtab[0][3];
    cout << "     [3] ";
    pFun();

    pFun = (Fun)pVtab[0][4];
    cout << "     [4] ";
    cout<<pFun<<endl;

    cout << "[1] Base1.b1_data = " << (int)pVtab[1] << endl;

    int s = sizeof(Base1)/4;

    cout << "\n[" << s << "] Base2::_vptr->"<<endl;
    pFun = (Fun)pVtab[s][0];
    cout << "     [0] ";
    pFun();

    pFun = (Fun)pVtab[s][1];
    cout << "     [1] ";
    pFun();

    pFun = (Fun)pVtab[s][2];
    cout << "     [2] ";
    pFun();

    pFun = (Fun)pVtab[s][3];
    cout << "     [3] ";
    cout<<pFun<<endl;

    cout << "["<< s+1 <<"] Base2.b2_data = " << (int)pVtab[s+1] << endl;

    s = s + sizeof(Base2)/4;

    cout << "[" << s << "] Base3::_vptr->"<<endl;
    pFun = (Fun)pVtab[s][0];
    cout << "     [0] ";
    pFun();

    pFun = (Fun)pVtab[s][1];
    cout << "     [1] ";
    pFun();

    pFun = (Fun)pVtab[s][2];
    cout << "     [2] ";
    pFun();

    pFun = (Fun)pVtab[s][3];
    cout << "     [3] ";
    cout<<pFun<<endl;

    s++;
    cout << "["<< s <<"] Base3.b3_data = " << (int)pVtab[s] << endl;
    s++;
    cout << "["<< s <<"] Derive.d_data = " << (int)pVtab[s] << endl;

    return 0;
}

实验结果如下图:

在这里插入图片描述
上图说明了以下几点内容:
(1)、红色圈圈表明了,子类会继承父类所有的虚表指针,从某种程度上来说也算是继承了其虚函数表。同时,如果子类有新增的虚函数,则就会添加在第一个继承的虚函数表中。

(2)、蓝色圈圈表明了,就多继承来说,子类会继承父类的虚表指针和其数据成员,并且按照声明的顺序依次排布。

(3)、黄色圈圈表明了,子类继承下来的虚函数表,只有最后一个虚函数表末尾会置空为0.

 
 
有点蒙? 来,一图胜千言。
在这里插入图片描述

1.7 多重继承有虚函数覆盖

这里的情况就是在上面多重继承的基础上,子类会覆盖父类的虚函数。

根据 参考【2】设计了下面的继承图。
在这里插入图片描述
实验代码只需要稍微修改一下Derive类,使其重写一些父类的一些函数如下:

Derive类

class Derive:public Base1,public Base2,public Base3{
public:

    Derive(int val = 100):d_data(val){
        cout<<"constructor in derive\n";
    }
    virtual void f(){cout << "virtual Derive::f" << endl;}      //重写子类的虚函数
    virtual void h1(){cout << "virtual Derive::h1" << endl;}    //重写子类的虚函数
    virtual void i(){cout << "virtual Derive::i" << endl;}      //增添的虚函数
    int d_data;       //数据成员

};

 
实验结果如下:
在这里插入图片描述
 
 
这里就有一点要说明的,子类中重写父类的函数,对应继承下来的虚函数表都需要进行更改。 示意图如下:
在这里插入图片描述


en… 没想到这第一部分还没写完,看起来有点多,还得放在下一篇博客来总结虚继承的情况了。

参考

【1】、C++ 虚函数表解析
【2】、C++ 对象的内存布局(上)
【3】、C++ 对象的内存布局(下)

C++本科毕业设计-ROS机械臂上位机源码(下载即用),个人经导师指导并认可通过的高分设计项目,评审分98分,项目中的源码都是经过本地编译过可运行的,都经过严格调试,确保可以运行!主要针对计算机相关专业的正在做大作业、毕业设计的学生和需要项目实战练习的学习者,资源项目的难度比较适中,内容都是经过助教老师审定过的能够满足学习、使用需求,如果有需要的话可以放心下载使用。 C++本科毕业设计-ROS机械臂上位机源码(下载即用)C++本科毕业设计-ROS机械臂上位机源码(下载即用)C++本科毕业设计-ROS机械臂上位机源码(下载即用)C++本科毕业设计-ROS机械臂上位机源码(下载即用)C++本科毕业设计-ROS机械臂上位机源码(下载即用)C++本科毕业设计-ROS机械臂上位机源码(下载即用)C++本科毕业设计-ROS机械臂上位机源码(下载即用)C++本科毕业设计-ROS机械臂上位机源码(下载即用)C++本科毕业设计-ROS机械臂上位机源码(下载即用)C++本科毕业设计-ROS机械臂上位机源码(下载即用)C++本科毕业设计-ROS机械臂上位机源码(下载即用)C++本科毕业设计-ROS机械臂上位机源码(下载即用)C++本科毕业设计-ROS机械臂上位机源码(下载即用)C++本科毕业设计-ROS机械臂上位机源码(下载即用)C++本科毕业设计-ROS机械臂上位机源码(下载即用)C++本科毕业设计-ROS机械臂上位机源码(下载即用)C++本科毕业设计-ROS机械臂上位机源码(下载即用)C++本科毕业设计-ROS机械臂上位机源码(下载即用)C++本科毕业设计-ROS机械臂上位机源码(下载即用)C++本科毕业设计-ROS机械臂上位机源码(下载即用)C++本科毕业设计-ROS机械臂上位机源码(下载即用)C++本科毕业设计-ROS机械臂上位机源码(下载即用)C++本科毕业设计-ROS机械臂上位机源
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值