探索MinGW是如何存储对象、如何实现类继承、多态

探索MinGW是如何存储对象、如何实现类继承、多态

一、需求分析

对象,继承,多态是面向对象编程语言中的一些概念,MinGW只是一个编译环境,它可以编译 C++ 程序,C++是一个面向对象的编程语言,所以比较正确的说法是 “C++是如何存储对象、如何实现类继承、多态“。

1、C++ 类数据成员存储分配

2、C++ 类虚函数存储分配

3、C++ 类继承存储分配

4、C++多态

二、概要设计

C++ 在 C 语言的基础上增加了面向对象编程,C++ 支持面向对象程序设计。类是 C++ 的核心特性,通常被称为用户定义的类型。类用于指定对象的形式,它包含了数据表示法和用于处理数据的方法。类中的数据和方法称为类的成员。函数在一个类中被称为类的成员。

类提供了对象的蓝图,所以基本上,对象是根据类来创建的。声明类的对象,就像声明基本类型的变量一样。

面向对象程序设计中最重要的一个概念是继承。继承允许我们依据另一个类来定义一个类,这使得创建和维护一个应用程序变得更容易。这样做,也达到了重用代码功能和提高执行时间的效果。

当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。

三、详细设计

1、基本数据类型所占的字节

我用的编译环境是32位的MinGW
 char :1个字节,char*(即指针变量): 4个字节,short int : 2个字节,int :4个字节,unsigned int : 4个字节,float : 4个字节,double: 8个字节,long : 4个字节,long long : 8个字节,unsigned long : 4个字节

2、对象占有一定的内存,该内存上存放的是该对象的相关数据,按先后顺序如下:

(1)虚表指针:如果该类有虚函数的话,将存放虚表指针,该指针指向该类的虚函数表,即指向表中的第一个元素。虚表中存放的是该类虚函数的地址;

(2)基类数据成员(如果有基类);

(3)自己的数据成员;

3、C++内存对齐

对齐是为了提高效率,计算机从内存中取数据是按照一个固定长度的。以32位机为例,它每次取32位,也就是4个字节(每字节8位)。字节对齐的好处,以double型数据为例,如果它在内存中存放的位置按4字节对齐,也就是说1个double的数据全部落在计算机一次取数的区间内,那么只需要取一次就可以。如果不对齐,这个double数据刚好跨越了取数的边界,这样需要取两次才能把这个double的数据全部取到,这样效率就降低了。

4、内存对齐规则

(1)数据成员对齐规则

结构(struct或union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储),基本类型不包括struct/class/uinon。

  1. 结构体作为成员

如果一个结构里有某些结构体成员,则结构体成员要从其内部“最宽基本类型成员”的整数倍地址开始存储(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储)。

  1. 收尾工作

结构体的总大小

  1. sizeof(union)

5、C++ 类继承存储分配

(1)C++ 类虚函数存储分配

将析构函数改成virtual,即在类中引入虚函数,为了将来的多态应用,编译器在数据成员前增加了一个指针vptr,指向vtbl(表中存放了各个虚函数的地址等信息)。

所以,对象占用的空间除了分配给数据成员,还有编译器根据需要安插进的空间。

(2)C++ 类单继承存储分配

在这种情况下,子类对象可以直接转换成父类对象,因为从子类对象的内存布局上看,上面是父类数据,下面是子类对象新添加的数据。转换的过程只是将子类对象指针赋给父类对象指针的过程。但是将父类对象转换成子类对象是很危险的,也是一般不被允许的。在android binder机制中获取本地代理对象时,拿到的就是最顶层父类IBinder的引用,其实这里就是子类对象的地址。

(3)C++ 类多继承存储分配

多继承是指 一个子类有两个或以上直接父类时称这个继承关系为多继承。这种继承方式使一个子类可以继承多个父类的特性。多继承可以看作是单继承的扩展。派生类具有多个基类,派生类与每个基类之间的关系仍可看作是一个单继承。多继承下派生类的构造函数与单继承下派生类构造函数相似,它必须同时负责该派生类所有基类构造函数的调用。同时,派生类的参数个数必须包含完成所有基类初始化所需的参数个数。在子类的内存中它们是按照声明定义的顺序存放的

出子类对象数据的分布和声明的继承顺序有关,越靠近子类越在上面

(4)C++ 类继承+虚拟存储分配

将析构函数改成virtual,即在类中引入虚函数,为了将来的多态应用,编译器在数据成员前增加了一个指针vptr,指向vtbl(表中存放了各个虚函数的地址等信息)。所以,对象占用的空间除了分配给数据成员,还有编译器根据需要安插进的空间。

C++ 类中有虚函数的时候有一个指向虚函数的指针(vptr),在32位系统分配指针大小为4字节。无论多少个虚函数,只有这一个指针,4字节。//注意一般的函数是没有这个指针的,而且也不占类的内存。

在C++中,如果类中有虚函数,那么它就会有一个虚函数表的指针__vfptr,在类对象最开始的内存数据中。之后是类中的成员变量的内存数据。 对于子类,最开始的内存数据记录着父类对象的拷贝(包括父类虚函数表指针和成员变量)。 之后是子类自己的成员变量数据。 对于子类的子类,也是同样的原理。但是无论继承了多少个子类,对象中始终只有一个虚函数表指针。 

(5)C++ 虚基类存储分配

虚继承 是面向对象编程中的一种技术,是指一个指定的基类,在继承体系结构中,将其成员数据实例共享给也从这个基类型直接或间接派生的其它类,它可共享的特性,避免了拷贝多份相同的数据,从而解决菱形继承的二义性和数据冗余的问题。

虚基类不是在声明基类时声明的,而是在声明派生类时的指定继承方式时声明的,因为一个基类可以在派生一个派生类时作为虚基类,而在派生另一个派生类时不作为虚基类,

声明虚基类的一般形式:class 派生类名 : viryual 继承方式 基类名

  1. C++多态

当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。C++多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。

在生成一个新的对象时,编译器在构造函数中插入了初始化代码,初始化vptr,让vptr指向类的vtbl。这样,就可以实现多态。

猜你喜欢

转载自blog.csdn.net/qq_38971487/article/details/81258572
今日推荐