5.C++中类的数据成员和成员函数内存分布情况

发布时间 2023-08-02 21:51:44作者: CodeMagicianT

5.C++中类的数据成员和成员函数内存分布情况

  • 非静态成员的数据类型大小之和。
  • 编译器加入的额外成员变量(如指向虚函数表的指针)。
  • 为了边缘对齐优化加入的padding。

空类(无非静态数据成员)的对象的size为1, 当作为基类时, size为0。

C++类是由结构体发展得来的,所以他们的成员变量(C语言的结构体只有成员变量)的内存分配机制是一样的。

首先,类计算大小与C语言中struct计算大小的规则是一样的,都遵循内存对齐原则

类的属性与方法是分开存储的,内存给类实例化出的对象开辟空间时只开辟成员变量所占用的空间,类中的所有成员函数全部都会被放入公共代码区,并且会被此类域修饰。也就是说成员函数是不占空间的,在计算类实例化对象的空间时,只计算成员变量的大小。

这样的好处:可以避免每个对象都保存着相同的代码,造成空间浪费。参考:[(8条消息) 【C++】之类和对象 - 概念与存储空间_类的对象存储空间_Hello_World_213的博客-CSDN博客]

一个类对象的地址就是类所包含的这一片内存空间的首地址,这个首地址也就对应具体某一个成员变量的地址。(在定义类对象的同时这些成员变量也就被定义了),举个例子:

#include <iostream>
using namespace std;

class Person
{
public:
    Person()
    {
        this->age = 23;
    }
    void printAge()
    {
        cout << this->age <<endl;
    }
    ~Person(){}
public:
    int age;
};

int main()
{
    Person p;
    cout << "对象地址:"<< &p <<endl;
    cout << "age地址:"<< &(p.age) <<endl;
    cout << "对象大小:"<< sizeof(p) <<endl;
    cout << "age大小:"<< sizeof(p.age) <<endl;
    return 0;
}
//输出结果
//对象地址:0x7fffec0f15a8
//age地址:0x7fffec0f15a8
//对象大小:4
//age大小:4

1.空类占用1个字节的存储空间

问题:C++类的成员函数在定义对象前分配了存储空间吗?[(8条消息) c++类的成员函数在定义对象前分配了存储空间吗?_爱吃甜食_的博客-CSDN博客]

对于一般的类(非静态)来说,在定义类但还未创建对象的时候,类的所有成员(包括变量和函数)都占用着内存空间(准确地说占用着指令代码区),但不占用堆栈空间

而创建对象的时候,会根据对象的类型占用堆栈的空间(用传统模式创建对象会占用栈空间,用引用+new模式创建对象会占用堆空间,同时引用会保存在栈里)

对于静态(static)类来说,静态类是不能实例化创建对象的,所有的成员都是静态成员,也需要占用内存空间,但不在堆栈里,而是在内存的静态/全局区(这个区域用于存放所有的全局成员和静态成员)。

声明一个空类做测试:

#include <iostream>
using namespace std;

class A 
{

};

int main() 
{
	cout << sizeof(A) << endl;
	return 0;
}

输出:

1

原因:类中没有任何成员变量,占用的存储大小本该为0,但是如果是0,类实例化出的对象就不会在内存上占用空间,没有地址,也就无法区分这些对象。为了解决这个问题,编译器会给空类隐含加一个字节,保证用此类定义的对象都有一个独一无二的地址。

2.类的成员函数(非虚函数)不占用类的存储空间

在1的基础上增加类的成员函数做测试:

#include <iostream>
using namespace std;

class A
{
public:
    A() {}
    ~A() {}
    int func1() { return 0; }
    int func2() { return 0; }
};

int main() 
{
    cout << sizeof(A) << endl;

    return 0;
}

输出:

1

原因:成员函数(包括构造和析构函数)编译后存放在代码区,不占用类的存储空间。

3.类的静态成员变量不占用类的存储空间

在2的基础上定义一个静态成员变量做测试:

#include <iostream>
using namespace std;

class A
{
private:
    static int n;
public:
    A() {}
    ~A() {}
    int func1() { return 0; }
    int func2() { return 0; }
};

int main()
{
    cout << sizeof(A) << endl;

    return 0;
}

输出:

1

原因:类里的静态成员变量在全局数据区中分配空间,不占用类的存储空间,全局只有一份,不会随着类的实例化存储在每个对象里。

静态成员函数的存放问题:静态成员函数与一般成员函数的唯一区别就是没有this指针,因此不能访问非静态数据成员。

就像我前面提到的,所有函数都存放在代码区,静态函数也不例外。所有有人一看到 static 这个单词就主观的认为是存放在全局数据区,那是不对的。

4.类中的虚函数占用类的存储空间,但所占的空间不会随着虚函数的个数增长

在3的基础上定义3个虚成员函数做测试:

#include <iostream>
using namespace std;

class A
{
private:
    static int n;
public:
    A() {}
    ~A() {}
    int func1() { return 0; }
    int func2() { return 0; }
    virtual int func3() { return 0; }
    virtual int func4() { return 0; }
    virtual int func5() { return 0; }
};

int main()
{
    cout << sizeof(A) << endl;

    return 0;
}

输出:

1

原因:有虚成员函数的类实例化的时候,会有一个指针vptr指向虚函数表vtbl,而虚函数表里就存储着类中定义的所有虚函数。所以虚函数引起的额外内存占用就是指针vptr占用内存的大小,对于64位系统来讲,指针占用的内存空间为8,所以这里的结果是8。而且不管定义几个虚函数,额外的内存占用都只是vptr指针所占空间的大小。

总结:类对象所占用的空间只由以下3部分组成:

​ (1)类的非静态成员变量

​ (2)编译器所做的数据对齐处理

​ (3)虚函数带来的额外开销

​ 其他类内定义的成员函数,静态成员变量等,均不占用类对象的存储空间。

参考资料来源于:[(8条消息) C++类对象到底占多大存储空间呢_c++对象占用内存_haowunanhai的博客-CSDN博客]