欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

C++对象模型

最编程 2024-07-29 10:42:07
...

C++对象模型

何为C++对象模型?

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

1.        语言中直接支持面向对象程序设计的部分

2.        对于各种支持的底层实现机制

语言中直接支持面向对象程序设计的部分,如构造函数析构函数虚函数继承(单继承、多继承、虚继承)、多态等等,这也是组里其他同学之前分享过的内容。第一部分这里我简单过一下,重点在底层实现机制。

c语言中,数据处理数据的操作(函数)是分开来声明的,也就是说,语言本身并没有支持数据和函数之间的关联性。在c++中,通过抽象数据类型(abstract data typeADT),在类中定义数据和函数,来实现数据和函数直接的绑定。

概括来说,C++类中有两种成员数据:staticnonstatic;三种成员函数:staticnonstaticvirtual

如下面的Base类定义:

Base类定义:

#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类在机器中我们如何构建出各种成员数据和成员函数的呢?

基本C++对象模型

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

简单对象模型(a simple object model

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

表格对象模型(a table-driven object model

这 个模型在简单对象的基础上又添加了一个间接层。将成员分成函数和数据,并且用两个表格保存,然后对象只保存了两个指向表格的指针。这个模型可以保证所有的 对象具有相同的大小,比如简单对象模型还与成员的个数相关。其中数据成员表中包含实际数据;函数成员表中包含的实际函数的地址(与数据成员相比,多一次寻 址)。

C++对象模型

这个模型从结合上面2中模型的特点,并对内存存取空间进行了优化。在此模型中,non static 数据成员被放置到对象内部,static数据成员, static and nonstatic 函数成员均被放到对象之外。对于虚函数的支持则分两步完成:

1.      每一个class产生一堆指向虚函数的指针,放在表格之中。这个表格称之为虚函数表(virtual tablevtbl)。

2.      每一个对象被添加了一个指针,指向相关的虚函数表vtbl。通常这个指针被称为vptrvptr的设定(setting)和重置(resetting)都由每一个class构造函数析构函数拷贝赋值运算符自动完成。

另外,虚函数表地址的前面设置了一个指向type_info的指针,RTTIRun Time Type Identification)运行时类型识别是有编译器在编译器生成的特殊类型信息,包括对象继承关系,对象本身的描述,RTTI是为多态而生成的信息,所以只有具有虚函数的对象在会生成

这个模型的优点在于它的空间和存取时间的效率;缺点如下:如果应用程序本身未改变,但当所使用的类的non static数据成员添加删除或修改时,需要重新编译。

模型验证测试

为了验证上述C++对象模型,我们编写如下测试代码。

模型验证测试:

void test_base_model()

{

    Base b1(1000);

    cout << "对象b1的起始内存地址:" << &b1 << endl;

    cout << "type_info信息:" << ((int*)*(int*)(&b1) - 1) << endl;

    RTTICompleteObjectLocator str=

        *((RTTICompleteObjectLocator*)*((int*)*(int*)(&b1) - 1));

    //abstract class name from RTTI

    string classname(str.pTypeDescriptor->name);

    classname = classname.substr(4,classname.find("@@")-4);

    cout << classname <<endl;

    cout << "虚函数表地址:\t\t\t" << (int*)(&b1) << endl;

    cout << "虚函数表1个函数地址:\t" << (int*)*(int*)(&b1) << "\t即析构函数地址:" << (int*)*((int*)*(int*)(&b1)) << endl;

    cout << "虚函数表2个函数地址:\t" << ((int*)*(int*)(&b1) + 1) << "\t";

    typedef void(*Fun)(void);

    Fun pFun = (Fun)*(((int*)*(int*)(&b1)) + 1);

    pFun();

    b1.print();

    cout << endl;

    cout << "推测数据成员iBase地址:\t\t" << ((int*)(&b1) +1) << "\t通过地址取值iBase的值:" << *((int*)(&b1) +1) << endl;

    cout << "Base::getIBase(): " << b1.getIBase() << endl;

 

    b1.instanceCount();

    cout << "静态函数instanceCount地址: " << b1.instanceCount << endl;

}

根据C++对象模型,实例化对象b1的起始内存地址,即虚函数表地址。

l  虚函数表的中第1个函数地址是虚析构函数地址;

l  虚函数表的中第2个函数地址是虚函数print()的地址,通过函数指针可以调用,进行验证;

l  推测数据成员iBase的地址,为虚函数表的地址 + 1((int*)(&b1) +1)

l  静态数据成员和静态函数所在内存地址,与对象数据成员和函数成员位段不一样;

下面是测试代码输出:(从下面2个图验证了,上面的观点。)

注意:本测试代码及后面的测试代码中写的函数地址,是对应虚函数表项的地址,不是实际的函数地址。

图:测试代码输出结果

图:vs断点观察(注意看虚函数表中第一个函数的地址,名称与测试代码输出一致)

上面介绍并验证了基本的C++对象模型,引入继承之后,C++对象模型又