C++类在内存中的分配

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

在网上看到这样一道题:

这里写图片描述

答案是24字节。

做这道题要理解三个知识点:

一、32位和64位操作系统的区别:32位和64位指的是CPU一次处理数据的能力是32位还是64位,这里涉及到的是处理器运算位数。简单的说32位系统的地址总线是32位的,而64位系统的地址总线是64位的,这意味着一个指针所占用的空间是64位即8个字节。

二、sizeof是用来计算栈大小,不涉及全局区,所以类的静态成员大小sizeof不计算。也就是说,类的成员变量中,只考虑a、p、func1、func2。

三、C++类在内存中的空间分配,这也是本文讲述的重点,下面着重阐述。

注意点1:一个类对象的地址就是类所包含的这一片内存空间的首地址,这个首地址对应具体该类某一个成员变量的地址。
  
  首先看下面这段代码:

#include <iostream> 
using namespace std;

class TEST
{
public:
    TEST(){
        t = 5;
    }
    int t;
}; 

int main()
{
    TEST test; 
    printf("%d--%d\n",&test,&test.t);
    printf("%d--%d\n",sizeof(test),sizeof(test.t));
    int *i = (int*)(&test);
    printf("%d\n",*i); 
}

  三个输出结果分别是2358848–2358848、4–4和5。来看看这三个输出分别代表什么意义。
  第一个输出&test代表的是类对象的地址,为2358848,与之对比的是&test.t代表的是TEST对象的唯一一个成员变量t的地址,发现和&test一模一样,说明类的地址就是类的第一个成员变量的地址。
  第二个输出对比的是类占用的栈空间大小和类中唯一一个成员变量的大小,均为4,即一个int变量的大小,说明类的大小取决于类中成员变量的大小。
  第三个输出为类的指针指向的值,为5,正好是类对象中成员变量t的值,说明类的地址对应类的某一个成员变量的值。
  那么问题来了,如果类中没有成员变量呢,是不是类的大小就是0了呢?来试试这段代码:

#include <iostream> 
using namespace std;

class TEST
{}; 

int main()
{
    TEST test; 
    printf("%d\n",sizeof(test));
    int *i = (int*)(&test);
    printf("%d\n",*i); 
}

  输出为1和603866880,可见即使类中没有成员变量,其大小也不会是0,这是有实际意义的,如果大小为0,那么空类数组的内存分配会比较麻烦。后面输出该类地址对应的值,为随机数,说明虽然大小为1,但是那个内存空间的值是随机的。
  
注意点2:类的成员函数不占用栈空间。
  
  所有的函数都是存放在代码区的, 不管是全局函数,还是成员函数,静态函数。所以类的成员函数是不占用栈的空间的,这很好理解,设想我们随便创建一个类,C++会自动生成默认构造函数和析构函数等一系列函数,这部分在我们之前测试类大小的时候并没有体现出来。

类本身是不占有内存的,可是 如果类生成实例那么将会在内存中分配一块内存来存储这个类。

所以A这样一个空类,编译器会给它一个字节来填充。

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

(1) 对于基类,如果有虚函数,那么先存放虚函数表指针,然后存放自己的数据成员;如果没有虚函数,那么直接存放数据成员。
(2) 对于单一继承的类对象,先存放父类的数据拷贝(包括虚函数表指针),然后是本类的数据。
(3) 虚函数表中,先存放父类的虚函数,再存放子类的虚函数
(4) 如果重载了父类的某些虚函数,那么新的虚函数将虚函数表中父类的这些虚函数覆盖。
(5) 对于多重继承,先存放第一个父类的数据拷贝,在存放第二个父类的数据拷贝,一次类推,最后存放自己的数据成员。其中每一个父类拷贝都包含一个虚函数表指 针。如果子类重载了某个父类的某个虚函数,那么该将该父类虚函数表的函数覆盖。另外,子类自己的虚函数,存储于第一个父类的虚函数表后边部分。
(6) 当对象的虚函数被调用是,编译器去查询对象的虚函数表,找到该函数,然后调用。

展开阅读全文

没有更多推荐了,返回首页