当前位置: 首页 > news >正文

从C++开始的编程生活(8)——内部类、匿名对象、对象拷贝时的编译器优化和内存管理

前言

本系列文章承接C语言的学习,需要有C语言的基础才能学会哦~
第8篇主要讲的是有关于C++的内部类匿名对象对象拷贝时的编译器优化内存管理
C++才起步,都很简单!!


目录

前言

内部类

性质

匿名对象

性质

※对象拷贝时的编译器优化

内存管理

内存分区

​编辑

内核空间

静态区/数据段

常量区/代码段

虚拟进程地址空间

动态内存管理函数

C++内存管理

基本语法

operator new和operator delete函数

new和delete的原理

定位new表达式(placement-new)


内部类

一个类定义在另一个类里,这个类就是内部类。

//简单代码
class A
{public:private:class B{private:int _b;}
}

性质

内部类默认是外部类的友元
本质是一种封装方式。若类B实现出来是为了给类A使用,那么可以把B设置为A的内部类。
内部类受访问限定符限制,被privact和protect修饰的内部类只能被外部类使用。

匿名对象

没有标识符标识的对象为匿名对象,反之为有名对象。

//有名对象,d1为其名
Date d1(1999,9,9);
//匿名对象
Date(2000,1,1);

性质

匿名对象的生命周期只有一行代码。也就是匿名对象构造后,下一步就会被析构(一次性筷子)。
②匿名对象具有常性,只能被const引用
被const引用后,匿名对象的生命周期被延长至与该引用同步。

※对象拷贝时的编译器优化

现代编译器会在不影响代码正确性的情况下,提高效率,在底层进行优化实现。
不同的编译器,优化方式不同,以下优化方式以VS2019的debug为例。
越新的编译器,优化越好,甚至会有跨多行合并优化代码。

例1:隐式类型转换

A aa1 = 1;//隐式类型转换

语法上:该隐式类型转换先以1为参数构造临时对象,在将临时对象拷贝构造给aa1。
实际上:直接以1为参数构造新对象aa1。
从而减少对内存的使用,提高代码运行效率。

例2:传值传参

void func(A aa)
{//·······
}
int main()
{func(1)return 0;
}

语法上:该传值传参先以1为参数构造临时对象,在将临时对象拷贝构造给aa,然后传入func。
实际上:直接以1为参数构造对象aa传入func。
从而减少对内存的使用,提高代码运行效率。

例3:传值返回

A f2()
{A aa;return aa
}int main()
{A aa2 = f2();return 0;
}

语法上:构造f2函数局部域的一个对象aa,然后返回aa时将aa拷贝构造为临时对象,接着临时对象在拷贝构造给aa2。(构造->拷贝->拷贝)。
实际上:构造f2函数局部域的一个对象aa,再直接拷贝给aa2。

内存管理

内存分区

内存分区主要分为四个区(还有其他不常用的区,这里省去):栈区、堆区、静态区和常量区

内核空间

用户代码,不可以读写。

局部变量,参数,返回值,对象,函数调用等等要在栈区开辟栈帧,主要存储临时的、局部的变量(栈区占比不大,M为单位)。栈向下增长,越后开辟的空间地址越小。

malloc、new等动态内存管理开辟的空间,在堆区(占比较大,以G为单位)。堆向上增长,越后开辟的空间地址越大(可能会在动态内存管理时被打乱顺序)

静态区/数据段

全局数据和静态数据放在静态区。

常量区/代码段

存放常量和编译完成的指令

虚拟进程地址空间

地址空间是虚拟的,需要页表等算法进行映射,这个部分知识涉及操作系统。

int a;
static int b = 1;
int main()
{int num[] = {1,2,3,4};static int c = 1;char arr[] = "abcdefg";const char* p1 = "abcdefg";char* p2 = (char*)malloc(sizeof(char) * 10);
}

如上代码
a、b、c存放在静态区
num、arr数组存放在栈区
p2存放在堆区
“abcdefg”存放在常量区

动态内存管理函数

C语言动态内存管理函数 malloc / calloc / realloc / free 依旧可以使用

tips:malloc / calloc / realloc的区别?------>calloc会在malloc的基础上,初始化开辟空间;realloc重新分配更多的空间,也覆盖malloc的功能。

C++内存管理

C++有新的方式进行内存管理,使用 new / delete 操作符进行内存管理。

基本语法

int main()
{//动态申请1个int类型空间int *ptr1 = new int;//动态申请1个int类型空间,并初始化为10int *ptr2 = new int(10);//释放开辟的int类型空间delete ptr1;delete ptr2;//动态申请3个int类型的空间int ptr3 = new int[3];//动态申请3个int类型的空间,并依次初始化为1,2,3int ptr4 = new int[3]{1,2,3};//动态申请5个int类型的空间,并依次初始化为1,2,3,为指定初始化的空间,默认初始化为0int ptr5 = new int[5]{1,2,3};//释放开辟的int类型数组delete[] ptr3;delete[] ptr4;delete[] ptr5;return 0;
}

以上为new和delete的使用,改为malloc和free基本没有区别。

而new开辟自定义类型:

//malloc开辟
A* p1 = (A*)malloc(sizeof(A));
//new开辟
A* p2 = new A;
A* p3 = new A(10);
A* p4 = new A[10];//申请空间,构造10次
A* p5 = new A[2]{1,2};//申请空间,构造2次
A* p6 = new A[2]{p2,p3};//申请空间,构造2次

区别:new之于malloc,不只是申请空间,还会调用其默认构造函数。

而delete释放自定义类型:

delete p2;
delete p3;
delete[] p4;//申请空间,析构10次
delete[] p5;//申请空间,析构2次
delete[] p6;//申请空间,析构2次

区别:delete之于free,不只是释放空间,还会调用其析构函数。

综上,对于自定义类型,还是优先使用new和delete。

operator new和operator delete函数

这不是对操作符new和delete的重载,它们实际上是两个全局函数使用new和delete时,在底层会调用相应的全局函数

在底层源码中,new实际上是对malloc的再次扩展封装。
malloc错误后会抛出一个nullptr,new会遇到malloc抛出的空指针后,首先执行用户提供的应对措施,如果不提供,就抛出一个异常。(异常为后面的内容,了解即可)。

在底层源码中,delete实际上是对free的再次扩展封装。
进行错误检查等处理后,再通过free释放空间

operator new和operator delete也可以直接调用。

new和delete的原理

调用new:①调用operator new函数,开辟空间。②若开辟自定义类型空间,最后依次调用其默认构造函数。

调用delete:①若是释放自定义类型空间,首先依次调用其析构函数。②调用operator delete函数,释放空间。

定位new表达式(placement-new)

作用是在已分配的原始内存空间调用构造函数初始化一个对象。

A *p1 = (A*)operator new(sizeof(A));
//new + 需构造的指针 + 对象类型 + 初始化参数
new(p1)A(10);

为什么要这样做呢?因为不是new开辟的对象,不可以直接初始化,必须要用这种格式

p1->A(10);//错误的

但是,析构可以直接调用。

p1->~A();

❤~~本文完结!!感谢观看!!接下来更精彩!!欢迎来我博客做客~~❤


文章转载自:

http://jmfBMbWO.mkyny.cn
http://BFPUtQdD.mkyny.cn
http://rbw9hQCK.mkyny.cn
http://MYKhhtcS.mkyny.cn
http://D38lnUru.mkyny.cn
http://K4587yX8.mkyny.cn
http://53MhK5h1.mkyny.cn
http://1DTbT3aP.mkyny.cn
http://QXCEBDuO.mkyny.cn
http://J5NPOWCC.mkyny.cn
http://79LldP3y.mkyny.cn
http://BKAjZNP4.mkyny.cn
http://qKjUtX93.mkyny.cn
http://CZIIXypu.mkyny.cn
http://IGjbTk1n.mkyny.cn
http://kBBmbyl2.mkyny.cn
http://nMBLN7Rc.mkyny.cn
http://ot2nxJRT.mkyny.cn
http://2WXFDbTn.mkyny.cn
http://W5xXnlYd.mkyny.cn
http://8SX4RXkN.mkyny.cn
http://FGHEmIBR.mkyny.cn
http://H67i8ri8.mkyny.cn
http://Pia4pYgz.mkyny.cn
http://n6GIX4KX.mkyny.cn
http://elGMWrRt.mkyny.cn
http://HWuvDRLU.mkyny.cn
http://gH4lIQP5.mkyny.cn
http://Wm4x3xuN.mkyny.cn
http://boWoPK70.mkyny.cn
http://www.dtcms.com/a/376590.html

相关文章:

  • 【AI时代速通QT】第六节:Qt Creator从添加新窗口到项目构建运行配置
  • 【CVPR 2022】面向2020年代的卷积神经网络
  • 图神经网络介绍
  • FPGA入门到进阶:可编程逻辑器件的魅力
  • 【解决问题】Ubuntu18上无法运行arm-linux-gcc
  • 嵌入式学习day47-硬件-imx6ull-LED
  • 深入体验—Windows从零到一安装KingbaseES数据库
  • 力扣习题——电话号码的字母组合
  • Linux环境下爬虫程序的部署难题与系统性解决方案
  • 深入解析ThreadLocal:线程数据隔离利器
  • D01-【计算机二级】Python(1)基本操作第41题
  • API开发工具postman、国内xxapi和SmartApi的性能对比
  • Scikit-learn Python机器学习 - 分类算法 - 线性模型 逻辑回归
  • SciKit-Learn 全面分析 digits 手写数据集
  • 《sklearn机器学习——数据预处理》标准化或均值去除和方差缩放
  • 保序回归Isotonic Regression的sklearn实现案例
  • 《sklearn机器学习——数据预处理》离散化
  • 无人机桨叶转速技术要点与突破
  • GPFS存储服务如何使用及运维
  • ELK 日志采集与解析实战
  • BI数据可视化:驱动数据价值释放的关键引擎
  • FinChat-金融领域的ChatGPT
  • OpenTenBase日常操作锦囊(新手上路DML)
  • Dart 中的 Event Loop(事件循环)
  • C++/Java编程小论——方法设计与接口原则总结
  • Java-Spring入门指南(四)深入IOC本质与依赖注入(DI)实战
  • 线扫相机采集图像起始位置不正确原因总结
  • JVM 对象创建的核心流程!
  • 秋日私语:一片落叶,一个智能的温暖陪伴
  • springCloud之配置/注册中心及服务发现Nacos