类和对象——类的对象占用内存的大小计算

类的对象大小的计算

  • 类的对象大小的计算
    • 1 案例分析
    • 2 如何计算类对象的大小
      • 案例分析中的猜测
      • 结构体内存对齐规则

类的对象大小的计算

1 案例分析

#include<iostream>class Date {
public:void Init(int year, int mouth, int day) {year = year;_mouth = mouth;day_ = day;}void print() {;}
private:int year;int _mouth;int day_;
};int main() {using std::cout;Date d;cout << sizeof(Date) << '\n';cout << sizeof(d) << '\n';d.print();return 0;
}

输出:
请添加图片描述

说明函数并没有算进来,连指针都没有。

c++的类遵循c语言的结构体的对齐规则。

这里涉及到公共的和私有的区别。对象各自的成员变量不是同一个,但对象各自的成员函数是同一个。

若在每一个对象里安置一个函数的内存并不划算。

类比的话,小区的健身房、泳池等都是公共的,但小区的卧室、客厅每家都各有一个。

每家都有一个泳池、健身房不划算,除非特别有钱。

成员函数就是公共的,成员变量是每个对象独有的。

所以d.print();是在代码的公共区去找。

没有类的函数表的概念,只有公共的代码区域,和全局函数一样都是存在那个区域。

从进程的角度叫代码钻(?扯到Linux了)。

2 如何计算类对象的大小

案例分析中的猜测

例如这个类:

class A {
public:void PrintA() {using std::cout;cout<<_a<<"\n";}
private:char _a;
};

类对象的存储方式猜测:

  1. 对象中包含类的各个成员。
    请添加图片描述

缺陷:每个对象中成员变量是不同的,但是调用同一份函数,如果按照此种方式存储,当一个类创建多个对象时,每个对象中都会保存一份代码(特别是函数的定义),相同代码保存多次,浪费空间。

  1. 代码只保存一份,在对象中保存存放代码的地址。

请添加图片描述

相比第1种猜测,这种方式节省了函数的实现,但可能带来大量的指针变量占用空间。

  1. 只保存成员变量,成员函数存放在公共的代码段
    请添加图片描述

问题:对于上述三种存储方式,那计算机到底是按照那种方式来存储的?

可以通过对下面的不同对象分别获取大小来分析。

#include<iostream>
// 类中既有成员变量,又有成员函数
class A1 {
public:void f1() {}
private:int _a;
};// 类中仅有成员函数
class A2 {
public:void f2() {}
};
// 类中什么都没有---空类
class A3
{};int main() {using std::cout;cout << sizeof(A1) << ' ' <<sizeof(A2) << ' ' << sizeof(A3) << '\n';//输出什么?return 0;
}

输出(vs2019环境下):
请添加图片描述

为什么输出1?因为类要进行一个占位。c++规定这种没有成员变量的类需要分配1个字节进行占位,表示对象存在过。

这1个字节存什么不重要,标准并没有规定存的什么,这个字节也无法访问(通过非常特殊的手段比如取地址再强转等等)。

如果一个空间都不开,两个对象的区别无法表示。

例如:

class A3
{};void f() {using std::cout;A3 a1;A3 a2;cout << &a1 << '\n' << &a2 << "\n";
}

若输出结果不同,则a1, a2是两个不同的对象。

结果说明了猜测3是正确的。

结论:一个类的大小,实际就是该类中”成员变量之和,当然要注意内存对齐。注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类的对象。

验证类中成员函数是否是同一个:

#include<iostream>
#include<cstdio>class A1 {
public:void Cout() {;}void Print();
private:int a;
};void A1::Print() {;
}void f2() {A1 a,b;a.Cout();b.Cout();
}int main(){f2();//调用反汇编即可查看return 0;
}

结果:
请添加图片描述

两个对象调用的同一成员函数的地址是一样的,进一步验证了第3个猜测。

结构体内存对齐规则

和c语言的对齐规则一样:

  1. 第一个成员在与结构体偏移量为0的地址处。

  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。

注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。vs2019默认的对齐数为8

  1. 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。

  2. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

例如这个例子,按照对齐规则,4字节的int型成员变量和1字节的char型成员变量组成的类的大小为8。

#include<iostream>
class A {
private:char _c;int _a;
};int main() {using std::cout;A a;cout << sizeof(a) << "\n";return 0;
}

结构:
请添加图片描述

因为cpu访问内存时,并不是从任意位置随意访问。比如某设备一次访问32bit,从结构体的起始位置,以4byte为单位进行访问(不一定是4字节,这个需要看设备,但可以肯定是整数倍),若不对齐的话,因为cpu只能通过设计好的位置去访问。不对齐的数据会降低数据的访问效率。

无论对齐不对齐,都只能从指定位置去访问。
请添加图片描述

例如不对齐的情况下,访问a:假设设备一次读4个字节,第一次读取出除了_c的另外3个字节,再读一次取出第1个字节,最后拼接在一起得到4个字节。效率大大降低。而且读两次还会访问到不需要的数据。

这个对齐数可以通过pragma指令进行修改。

#pragma pack(想要的默认对齐数) 

修改方式:

#pragma pack(1)
//修改偏移量为1
struct A {char c1;int i;char c2;
}A;
#pragma pack()
//使默认偏移量变回编译器的缺省值

案例:

#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS 1
#endif#include<iostream>
#include<cstdio>#pragma pack(1)
//修改偏移量为1
struct A {char c1;int i;char c2;
};
//定义完A后,使默认偏移量变回编译器的缺省值
#pragma pack()
struct B {//再定义B时是按照默认对齐数进行定义char c1;int i;char c2;
};
void f1() {std::cout << sizeof(A) << "\n";std::cout << sizeof(B) << "\n";
}void f2() {
#pragma pack(2)struct C {char c1;int i;char c2;};
#pragma pack()struct D {char c1;int i;char c2;};std::cout << sizeof(C) << "\n";std::cout << sizeof(D) << "\n";
}int main() {f1();f2();return 0;
}

输出结果:
请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/68824.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

nuxt3项目打包部署到服务器后配置端口号和开启https

nuxt3打包后的项目部署相对于一般vite打包的静态文件部署要稍微麻烦一些&#xff0c;还有一个主要的问题是开发环境配置的.env环境变量在打包后部署时获取不到&#xff0c;具体的解决方案可以参考我之前文章 nuxt3项目打包后获取.env设置的环境变量无效的解决办法。 这里使用的…

如何使用CRM数据分析和洞察来支持业务决策和市场营销?

如何使用CRM数据分析和洞察来支持业务决策和市场营销&#xff1f; 大家好&#xff01;今天咱们聊聊一个特别重要的话题——如何利用客户关系管理&#xff08;CRM&#xff09;系统中的数据进行分析与洞察能够帮助我们做出更好的业务决策以及提升市场营销效果。其实啊&#xff0…

关于linux的ld.so.conf.d

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 源码指引&#xff1a;github源…

STM32-CAN总线

1.CAN总线简介 CAN总线是由BOSCH公司开发的一种简洁易用、传输速度快、易扩展、可靠性高的串行通信总线 2.CAN总线特征 两根通信线&#xff08;CAN_H、CAN_L&#xff09;&#xff0c;线路少&#xff0c;无需共地差分信号通信&#xff08;相对的是单端信号&#xff09;&#…

在线宠物用品|基于vue的在线宠物用品交易网站(源码+数据库+文档)

|在线宠物用品交易网站 目录 基于springbootvue的在线宠物用品交易网站 一、前言 二、系统设计 三、系统功能设计 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大厂码农|毕设布道师&am…

StarRocks 怎么让特定的SQL路由到FE master节点的

背景 本文基于 StarRocks 3.1.7 大家都知道对于Starrocks来说 FE 是分 master和follower的&#xff0c;而只有master节点才能对元数据进行写操作。但是为什么呢&#xff1f;哪里有体现呢&#xff1f; 这其中的原因在网上是搜不到的&#xff0c;所以大家只知道只有master节点才…

JavaScript语言的正则表达式

JavaScript语言的正则表达式详解 正则表达式&#xff08;Regular Expression&#xff0c;简称Regex或RegExp&#xff09;是一种强大的文本处理工具&#xff0c;可以在字符串中执行模式匹配和替换操作。在JavaScript中&#xff0c;正则表达式是处理字符串时不可或缺的部分&…

Android SystemUI——快捷面板的创建(十四)

上一篇文章介绍了快捷面板界面 QSFragment 的创建流程,这里我们继续介绍快捷按键 QSTile 和管理 QSTile 生命周期和服务注册的 QSTileHost。 一、QSTileHost初始化 Android 9.0 以及之前的版本,实例化 QSTileHost 类是在 StatusBar 的 makeStatusBarView() 方法中。 1、Sta…

AI时代下 | 通义灵码冲刺备战求职季

AI时代下 | 通义灵码冲刺备战求职季 什么是通义灵码使用智能编程助手备战求职靠谱吗体验心得 AI时代下&#xff0c;备战求职季有了不一样的方法&#xff0c;使用通义灵码冲刺备战求职季&#xff0c;会有什么样的体验&#xff1f; 什么是通义灵码 在开始话题之前&#xff0c;首…

Qt之QDjango-db的简单使用

QDjango是一款由C编写、依托于Qt库的Web开发框架&#xff0c;其设计理念受到了广受欢迎的Python框架Django的影响。这个项目旨在提供一个高效、灵活且易于使用的工具集&#xff0c;帮助开发者构建高质量的Web应用。其项目地址: https://gitcode.com/gh_mirrors/qd/qdjango&…

冯诺依曼架构和哈佛架构的主要区别?

冯诺依曼架构&#xff08;Von Neumann Architecture&#xff09;和哈佛架构&#xff08;Harvard Architecture&#xff09;是两种计算机体系结构&#xff0c;它们在存储器组织、指令处理和数据存取等方面有明显的不同。以下是它们的主要区别&#xff1a; 1.存储器结构 冯诺依曼…

NoETL | 数据虚拟化如何在数据不移动的情况下实现媲美物理移动的实时交付?

在我们之前的文章中&#xff0c;我们回顾了Denodo在逻辑数据仓库和逻辑数据湖场景中所使用的主要优化技术&#xff08;具体内容请参阅之前的文章&#xff09;。 数据架构 | 逻辑数据仓库与物理数据仓库性能对比_物理数仓、逻辑数仓-CSDN博客文章浏览阅读1.5k次&#xff0c;点赞…

【Linux】Linux重要工具

Linux中一切皆文件&#x1f493;&#x1f493;&#x1f493; 目录 ✨说在前面 &#x1f34b;知识点一&#xff1a;Linux软件包管理器yum •&#x1f330;1. 什么是软件包 •&#x1f330;2. 查看软件包 •&#x1f330;3. 如何安装、卸载软件 &#x1f34b;知识点二&#…

JS通过ASCII码值实现随机字符串的生成(可指定长度以及解决首位不出现数值)

在之前写过一篇“JS实现随机生成字符串&#xff08;可指定长度&#xff09;”&#xff0c;当时写的过于简单和传统&#xff0c;比较粗放。此次针对此问题&#xff0c;对随机生成字符串的功能进行优化处理&#xff0c;对随机取到的字符都通过程序自动来完成。 在写之前&#xff…

K8S-Pod资源清单的编写,资源的增删改查,镜像的下载策略

1. Pod资源清单的编写 1.1 Pod运行单个容器的资源清单 ##创建工作目录 mkdir -p /root/manifests/pods && cd /root/manifests/pods vim 01-nginx.yaml ##指定api版本 apiVersion: v1 ##指定资源类型 kind: Pod ##指定元数据 metadata:##指定名称name: myweb ##用户…

【C++】在线五子棋对战项目网页版

目录 1.Websocket 1.1.Websocket的简单认识 1.2.什么是轮询呢&#xff1f; 1.3.websocket协议切换过程 1.4.websocketpp库常用接口认识 1.5.websocketpp库搭建服务器流程 1.6.websocketpp库搭建服务器 2.mysqlclient库-接口认识 3.项目模块的划分&#xff1a; 4.项目…

pytest+playwright落地实战大纲

前言 很久没有更新博客&#xff0c;是因为在梳理制作Playwright测试框架实战相关的课程内容。现在课程已经完结&#xff0c;开个帖子介绍下这门课程&#xff08;硬广, o(〃&#xff3e;▽&#xff3e;〃)o&#xff09; 课程放在CSDN学习频道&#xff0c; 欢迎关注~ PyTestPl…

数据结构-ArrayList和顺序表

1.线性表 线性表是n个具有相同类型的数据元素所组成的有限序列&#xff0c;当n0时&#xff0c;线性表为一个空表。 常见的线性表&#xff1a;顺序表&#xff0c;链表&#xff0c;栈和队列... 线性表在逻辑上是线性结构&#xff0c;可以说是连续的一条直线。但是在物理结构上…

红外热成像之无人机载荷

电力巡检 相较于传统的人工电力巡线方式&#xff0c;无人机巡检能够在高空对人工难以达到或无法检测的设备进行检测&#xff0c;实现了电子化、信息化、智能化巡检&#xff0c;可以提高巡检的工作效率和应急抢险水平。 森林防火 无人机搭载红外光电系统能在森林高空进行全天候监…

Typescript新特性关键字readyonly详细解读

readonly修饰符&#xff1a;首先是一个关键字&#xff0c;对类中的属性成员进行修饰&#xff0c;修饰后&#xff0c;该属性成员&#xff0c;就不能在外部被随意的修改了 一 构造函数中&#xff0c;可以对只读的属性成员的数据进行修改 (()>{//定义一个类型class Person {/…