首页 > 系统相关 > 5.C++中类的数据成员和成员函数内存分布情况

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

时间:2023-08-02 21:55:24浏览次数:50  
标签:函数 int 占用 C++ 静态 存储空间 成员 中类

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博客]

标签:函数,int,占用,C++,静态,存储空间,成员,中类
From: https://www.cnblogs.com/codemagiciant/p/17601861.html

相关文章

  • 128.用C语言实现C++的继承
    128.用C语言实现C++的继承#include<iostream>usingnamespacestd;//C++中的继承与多态structA{virtualvoidfun()//C++中的多态:通过虚函数实现{cout<<"A:fun()"<<endl;}inta;};structB:publicA//C++中的继承:B类公有继......
  • c++控制台时钟显示时间
    在旧电脑上刷了ubuntu的服务器版本,开着的时候旧电脑(yoga)的屏幕就处于了无用的状态。为了更好的利用这个屏幕,简单使用c++写了一个显示时间的代码。由于这台yoga的屏幕是可以360翻转的,所以给时间的显示也增加了一点翻转功能。1#include<iostream>2#include<ctime>3#......
  • 恶意代码分析实战 lab 20-1 20-2 20-3 C++恶意代码分析,难度提升
    本次实验我们将会分析lab20-1,lab20-2文件。先来看看要求解答的问题Lab20-1需要回答的问题如下Q1.在0x401040处的函数采用了什么参数?Q2.哪个URL被用来调用URLDownloadToFile?Q3.这个程序做了什么事情?在实验任务一当中我们来分析lab20-1首先载入IDA第一个call是在401008,是一个......
  • C++逆向分析——友元、内部类、命名空间和static
    友元友元可以理解为:朋友、元素;老师认为这个友元是C++中的一个垃圾,因为友元的存在破坏了面向对象的封装性,不推荐使用,之所以有这个章节是因为有人不了解这个概念。注意:在一些新版本的C++编译器里面已经不再提供类似于友元这样的特性了。大家都知道在C++中对象的私有成员,外部是无法访......
  • C++逆向分析——对象拷贝
    对象拷贝我们通常存储对象,都用数组、列表之类的来存储,那如下所示我们使用数组来存储对象,但是在工作中发现这个数组不够用了,就需要一个更大的数据,但我们重新创建一个数组还需要把原来的数据复制过来;在C语言中可以使用函数来进行拷贝,直接拷贝内存,在C++中实际上跟C语言要做的事情是一......
  • C++逆向分析——模版
    模版假设有一个冒泡排序的函数:voidSort(int*arr,intnLength){inti,k;for(i=0;i<nLength;i++){for(k=0;k<nLength-1-i;k++){if(arr[k]>arr[k+1]){inttemp=arr[k];arr[k]=arr[k+1];arr[k+1]=temp;}}}}但是这个冒......
  • C++逆向分析——运算符重载
    运算符重载现在有一个类,其中有一个函数用于比较2个类的成员大小:#include<stdio.h>classNumber{private:intx;inty;public:Number(intx,inty){this->x=x;this->y=y;}intMax(Number&n){returnthis->x>n.x&&this->y......
  • C++逆向分析——多态和虚表
    虚表上一章了解了多态,那么我们来了解一下多态在C++中是如何实现的。了解本质,那就通过反汇编代码去看就行了,首先我们看下非多态的情况下的反汇编代码:然后再来看下多态情况下的反汇编代码:很明显这里多态的情况下会根据edx间接调用,而非多态则会直接调用。那么我们来看下间接调用的流程......
  • C++逆向分析——继承与封装
    面向对象程序设计之继承与封装之前已经学习过继承和封装了,但是要在实际开发中使用,光学语法和原理是不够的,在设计层面我们需要做一些优化。如下代码是继承的例子:#include<stdio.h>classPerson{public:intAge;intSex;voidWork(){printf("Person:Work()"......
  • C++逆向分析——引用
    voidmain(){intx=1;int&ref=x;ref=2;printf("%d\n",ref);return;}反汇编代码:intx=1;00724A5FC745F401000000movdwordptr[x],1int&ref=x;00724A668D45F4lea......