精华内容
下载资源
问答
  • c++对象模型
    千次阅读
    2021-06-25 08:55:43

    C++对象模型

    1、何为C++对象模型?

    C++对象模型可以概括为以下2部分:

    • ① 语言中直接支持面向对象程序设计的部分
      • 面向对象程序设计部分:如构造函数析构函数虚函数继承(单继承、多继承、虚继承)、多态等。
    • ② 对于各种支持的底层实现机制

    在C++类中有两种数据成员、三种成员函数

    • 两种数据成员:static、nonstatic。
    • 三种成员函数:static、nonstatic、virtual。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WCVz1h2A-1624582513579)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210527111257338.png)]

    代码举例说明:

    //base.h
    #pragma once
    #include<iostream>
    using namespace std;
    class Base
    {
    public:
    	Base(int);
    	virtual ~Base(void);
    
    	int getIBase() const;
    	static int instanceCount();
    	virtual void print() const;
    
    protected:
    	int iBase;
    	static int count;
    };
    

    问:Base类在机器中我们如何构建出各种成员数据和成员函数的呢?

    2、基本C++对象模型

    在介绍C++使用的对象模型之前,介绍2种对象模型:简单对象模型(a simple object model)、表格驱动对象模型(a table-driven object model)。

    a)简单对象模型

    所有的成员占用相同的空间(跟成员类型无关),对象只是维护了一个包含成员指针的一个表。表中放的是成员的地址,无论上成员变量还是函数,都是这样处理。对象并没有直接保存成员而是保存了成员的指针。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LspY6TpE-1624582513583)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210527112146984.png)]

    b)表格驱动对象模型

    这个模型在 简单对象模型 的基础上又添加了 一个间接层

    将成员分成函数和数据,并且用两个表格保存,然后对象只保存了 两个指向表格的指针

    表格驱动对象模型 可以保证所有的 对象 具有相同的大小(只保存俩指针); 简单对象模型 的类对象还与成员的个数相关。

    • 其中数据成员表中包含实际数据;
    • 函数成员表中包含的实际函数的地址(与数据成员相比,多一次寻址)。

    c)C++对象模型

    在C++对象模型中:

    • ① nonstatic 数据成员被放置到对象内部;
    • ② static数据成员、static and nonstatic 函数成员均被放到对象之外。
    • ③ 对虚函数的支持分为两步:
      • a)每个class会为每个虚函数生成一个指针,这些指针统一放在虚函数表中(vtbl)
      • b)每个class的对象都会添加一个指针(vptr),指向相关的虚函数表(vtbl)。
        • vptr的设定(setting)和重置(resetting)都由每一个class的构造函数析构函数拷贝赋值运算符自动完成。
    • ④ 另外,虚函数表地址的前面设置了一个指向type_info类的指针。
      • C++提供了一个type_info类来获取对象类型信息。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OzJFHlm3-1624582513587)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210527114108905.png)]

    C++对象模型优点与缺点:

    • 优点:在于它的空间和存取时间的效率
    • 缺点:当所使用的类的non static数据成员添加删除或修改时,需要重新编译。

    d)模型验证测试

    class Base
    {
    public:
        Base(int i) :baseI(i){};
        virtual void print(void){ cout << "调用了虚函数Base::print()"; }
        virtual void setI(){cout<<"调用了虚函数Base::setI()";}
        virtual ~Base(){}
    private:
        int baseI;
    };
    
    • 当一个类本身定义了虚函数,或其父类有虚函数时,为了支持多态机制,编译器将为该类添加一个虚函数指针(vptr)。

    • 虚函数指针一般都放在 对象内存布局的第一个位置 上,这是为了保证在多层继承或多重继承的情况下能以最高效率取到虚函数表。

    • 当vptr位于对象内存最前面时,对象的地址即为虚函数指针地址。我们可以取得虚函数指针的地址:

      Base b(1000);
      int *vptrAddr = (int *)(&b);   //强行把类对象的地址转换为 int* 类型,取得了虚函数指针的地址。
      
    • 虚函数指针指向虚函数表,虚函数表中存储的是一系列虚函数的地址,虚函数地址出现的顺序与类中虚函数声明的顺序一致。

    • 对虚函数指针地址值,可以得到虚函数表的地址,也即是虚函数表第一个虚函数的地址:

      typedef void(*Fun)(void);
      Fun vfunc = (Fun)*( (int *)*(int*)(&b));
      /*
      取出虚函数表指针的值: *(int*)(&b);      它是一个地址,虚函数表的地址
      把虚函数表的地址强制转换成 int* :(int *)*(int*)(&b);
      再把它转化成我们Fun指针类型 : (Fun)*(int*)*(int*)(&b)
      */
      cout << "第一个虚函数的地址是:" << (int *)*(int*)(&b) << endl;
      cout << "通过地址,调用虚函数Base::print():";
      vfunc();
      

    3、C++模型中加入单继承

    class Base
    {
    public:
        Base(int i) :baseI(i){};
        virtual void print(void){ cout << "调用了虚函数Base::print()"; }
        //virtual void setI(){cout<<"调用了虚函数Base::setI()";}
        virtual ~Base(){}
    private:
        int baseI;
    };
    class Derive : public Base
    {
    public:
        Derive(int d) :Base(1000),      DeriveI(d){};
        //override父类虚函数
        virtual void print(void){ cout << "Drive::Drive_print()" ; }
        // Derive声明的新的虚函数
        virtual void Drive_print(){ cout << "Drive::Drive_print()" ; }
        virtual ~Derive(){}
    private:
        int DeriveI;
    };
    

    简单继承下有重写的C++对象模型: 无重写的话,在子类虚函数表中是不会覆盖父类虚函数的。

    继承类图为

    4、C++模型中加入多继承

    4.1 一般的多重继承(非菱形继承)

    从单继承可以知道,派生类中只是扩充了基类的虚函数表。如果是多继承的话,又是如何扩充的?

    • ① 每个基类都有自己的虚表
    • ② 子类的虚函数被放到了第一个基类的虚函数表中。
    • ③ 内存布局中,其父类布局依次按声明顺序排列。
    • ④ 每个基类的虚表中的print()函数都被overwrite成了子类的print ()。这样做就是为了解决不同的基类类型的指针指向同一个子类实例,而能够调用到实际的函数。
    class Base
    {
    public:
     
        Base(int i) :baseI(i){};
        virtual ~Base(){}
     
        int getI(){ return baseI; }
     
        static void countI(){};
     
        virtual void print(void){ cout << "Base::print()"; }
     
    private:
     
        int baseI;
     
        static int baseS;
    };
    class Base_2
    {
    public:
        Base_2(int i) :base2I(i){};
    
        virtual ~Base_2(){}
    
        int getI(){ return base2I; }
    
        static void countI(){};
    
        virtual void print(void){ cout << "Base_2::print()"; }
     
    private:
     
        int base2I;
     
        static int base2S;
    };
     
    class Drive_multyBase :public Base, public Base_2
    {
    public:
    
        Drive_multyBase(int d) :Base(1000), Base_2(2000) ,Drive_multyBaseI(d){};
     
        virtual void print(void){ cout << "Drive_multyBase::print" ; }
     
        virtual void Drive_print(){ cout << "Drive_multyBase::Drive_print" ; }
     
    private:
        int Drive_multyBaseI;
    };
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BcdzbFOm-1624582513591)(H:\Xmind共享\C++\入门\C++对象模型.assets\image-20210528101015496.png)]

    4.2 菱形继承

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wRNOyCnF-1624582513594)(H:\Xmind共享\C++\入门\C++对象模型.assets\image-20210528101444074.png)]

    D类对象内存布局中:

    • 图中青色表示b1类子对象实例,黄色表示b2类子对象实例,灰色表示D类子对象实例。

    • 由于D类间接继承了B类两次,导致D类对象中含有两个B类的数据成员ib,一个属于来源B1类,一个来源B2类。这样不仅增大了空间,更重要的是引起了程序歧义:

      D d;
      d.ib = 1;    //二义错误,调用的是B1::ib还是B2::ib?
      
      //正确调用方式
      d.B1::ib = 1;
      d.B2::ib = 1;
      

    5、C++模型中加入虚继承

    什么是虚继承

    //类的内容与前面相同
    class B{...}
    class B1 : virtual public B
    

    虚继承是为了解决 重复继承中多个间接父类 的问题的,所以不能使用上面简单的扩充并为每个虚基类提供一个虚函数指针(这样会导致重复继承的基类会有多个虚函数表)形式。

    虚继承的派生类的内存结构,和普通继承完全不同:

    • 虚继承的子类,有单独的虚函数表,另外也单独保存一份父类的虚函数表,两部分之间用一个四个字节的0x00000000来作为分界
    • 派生类的内存中,首先是自己的虚函数表,然后是派生类的数据成员,然后是0x0,之后就是基类的虚函数表,之后是基类的数据成员。
    • 如果派生类没有自己的虚函数,那么派生类就不会有虚函数表,但是派生类数据和基类数据之间,还是需要0x0来间隔。

    总结

    • 因此,在虚继承中,派生类和基类的数据,是完全间隔的,先存放派生类自己的虚函数表和数据,中间以 0x0 分界,最后保存基类的虚函数和数据。
    • 如果派生类重载了父类的虚函数,那么则将派生类内存中基类虚函数表的相应函数替换
    • 在C++对象模型中,虚继承而来的子类会生成一个隐藏的虚基类指针(vbptr)
    • 因而,对某个类实例来说,如果它有虚基类指针,那么虚基类指针可能在实例的0字节偏移处(该类没有vptr时,vbptr就处于类实例内存布局的最前面,否则vptr处于类实例内存布局的最前面),也可能在类实例的4字节偏移处。

    5.1 简单虚继承

    类图如下:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bYb0bFDV-1624582513596)(H:\Xmind共享\C++\入门\C++对象模型.assets\image-20210528103814876.png)]

    子类对象模型如下:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YLgSeCRy-1624582513599)(H:\Xmind共享\C++\入门\C++对象模型.assets\image-20210528103931087.png)]

    5.2 菱形虚继承

    类图如下:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pbN6POQi-1624582513601)(H:\Xmind共享\C++\入门\C++对象模型.assets\image-20210528104351682.png)]
    菱形虚拟继承下,最派生类D类的对象模型又有不同的构成了。在D类对象的内存构成上,有以下几点:

    • 在D类对象内存中,基类出现的顺序是:先是B1(最左父类),然后是B2(次左父类),最后是B(虚祖父类)
    • D类对象的数据成员id放在B类前面,两部分数据依旧以0来分隔。
    • 编译器没有为D类生成一个它自己的vptr,而是覆盖并扩展了最左父类的虚基类表,与简单继承的对象模型相同。
    • 超类B的内容放到了D类对象内存布局的最后。

    菱形虚拟继承下的C++对象模型为:

    更多相关内容
  • 总结笔记,关于侯捷翻译的《深入探索c++对象模型》的笔记 作者Lippman参与设计了全世界第一套C++编译程序cfront,这本书就是一位伟大的C++编译程序设计者向你阐述他如何处理各种explicit(明确出现于C++程序代码中)...
  • 深度探索c++对象模型(2012版本)
  • C++对象模型

    2017-07-30 12:11:23
    C++模型之菱形虚拟继承中的内存分布
  • 《深度探索C++对象模型》重点探索"对象导向程序所支持的C++对象模型"下的程序行为。对于"对象导向性质之基础实现技术"以及"各种性质背后的隐含利益交换"提供一个清楚的认识。检验由程序变形所带来的效率冲击。提供...
  • 深度探索C++对象模型 PDF中文清晰版.zip深度探索C++对象模型 PDF中文清晰版.zip深度探索C++对象模型 PDF中文清晰版.zip深度探索C++对象模型 PDF中文清晰版.zip深度探索C++对象模型 PDF中文清晰版.zip深度探索C++对象...
  • 深度探索C++对象模型 中文版+英文原版合集 PDF格式 高清版 有书签目录 无水印 完整版Inside the C++Object Model by Stanley.B.Lippman 侯捷 译 内容简介 · · · · · · 这本书探索“对象导向程序所支持的C++...
  • 深度探索C++对象模型
  • 本书重点:探索“对象导向程序所支持的C++对象模型”下的程序行为。对于“对象导向性质之基础实现技术”以及“各种性质背后的隐含利益交换”提供一个清楚的认识。检验由程序变形所带来的效率冲击。提供丰富的程序...
  • 深度探索C++对象模型PDF 2012 中文 高清 扫描版 带完整目录书签
  • C++对象模型总结

    2018-10-09 15:59:28
    C++对象模型 第1章 关于对象 第2章 构造函数语意学 第3章 Data语意学 第4章 Function语意学 第5章 构造、析构、拷贝语意学 第6章 执行期语意学 第7章 站在对象模型的尖端 第8章 C++对象模型总结 8.1 C++对象模型 8.2...
  • 作者Lippman参与设计了全世界第一套C++编译程序cfront,这本书就是一...书中涵盖了C++对象模型的语意暗示,并指出这个模型是如何影响你的程序的。 对于C++底层机制感兴趣的读者,这必然是一本让你大呼过瘾的绝妙好书。
  • 深度探索C++对象模型 基本信息 原书名:Inside the C++ Object Model 原出版社: Addison-Wesley Professional 作者: (美)Stanley B. Lippman (斯坦利.B.李普曼) 译者: 侯捷 丛书名: 传世经典书丛 出版社:...
  • 本书探索“对象导向程序所支持的C++对象模型”下的程序行为。对于“对象导向性质之基础实现技术”以及“各种性质背后的隐含利益交换”提供一个清楚的认识。检验由程序变形所带来的效率冲击。提供丰富的程序范例、...
  • 深度探索C++对象模型

    2019-02-14 23:25:39
    这本书探索“对象导向程序所支持的C++对象模型”下的程序行为。对于“对象导向性质之基础实现技术”以及“各种性质背后的隐含利益交换”提供一个清楚的认识。检验由程序变形所带来的效率冲击。提供丰富的程序范例、...
  • C++对象模型
  • C++对象模型在内存中的实现,讲述了类,继承以及虚继承的内存布局;成员变量和成员函数的访问已经访问时的开销情况,包含虚函数的情况,考察构造函数,析构函数,以及特殊的赋值操作符成员函数是如何工作的,数组是...
  • 这本书探索“对象导向程序所支持的C++对象模型”下的程序行为。对于“对象导向性质之基础实现技术”以及“各种性质背后的隐含利益交换”提供一个清楚的认识。检验由程序变形所带来的效率冲击。提供丰富的程序范例、...
  • 深度探索C++对象模型2012版 高清 pdf这一版扫描质量很高,足够清晰,而且是2012版噢,我上传的资源基本都是自己整理过书签或者去除水印的,主要与C++,设计模式,架构,QT等有关系,更多的可以去我的资源页看看。...
  • 图说C++对象模型.pdf

    2019-07-31 14:08:26
    网上看到的一篇博客,对C++对象内存模式讲的详细易懂。
  • NULL 博文链接:https://dsqiu.iteye.com/blog/1669614
  • 深度探索C++对象模型 PDF中文清晰版,内容简单易懂,更加深入的探讨C++对象模型

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 191,404
精华内容 76,561
关键字:

c++对象模型

c++ 订阅
友情链接: randomforest-master.zip