C++初阶:内存管理

目录

  • 1. C/C++中各种资源的内存分布
    • 1.1 C/C++程序内存区域划分
    • 1.2 各资源的内存分布情况(练习)
  • 2. C++中的动态内存管理方式
    • 2.1 new/delete开辟内置类型空间
    • 2.2 new/delete开辟销毁自定义类型空间
  • 3. operator new 与 operator delete函数
  • 4. new与delete的实现原理
  • 5. 定位new表达式与池化计数
  • 6. malloc/free与new/delete的异同

1. C/C++中各种资源的内存分布

1.1 C/C++程序内存区域划分

正在执行的程序是在计算机的内存空间上运行的,C/C++为了程序的高效运行,将内存划分了多个区域来进行对不同特性种类资源的区别管理。

在这里插入图片描述

1.2 各资源的内存分布情况(练习)

//全局变量,数据段
int globalVar = 1;//静态全局变量,数据段
static int staticGlobalVar = 1;
void Test()
{//静态变量,数据段static int staticVar = 1;//局部变量,栈int localVar = 1;//局部变量,数组,栈//sizeof(num1),代表整个数组,40字节int num1[10] = { 1, 2, 3, 4 };//局部变量,字符数组,栈//sizeof(char2),代表整个数组,5字节//strlen(char2),字符串长度,4char char2[] = "abcd";//sizeof(pChar3),指针,4/8字节//strlen(pChar3),字符串长度,4//只读字符串,代码段const char* pChar3 = "abcd";//动态开辟空间,堆//ptr1指针,4/8字节int* ptr1 = (int*)malloc(sizeof(int) * 4);//calloc会用0进行申请空间的初始化int* ptr2 = (int*)calloc(4, sizeof(int));int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);free(ptr1);free(ptr3);
}

2. C++中的动态内存管理方式

  1. 在编写程序时,我们时常需要一段可以动态增长我们可以控制其开辟与销毁的空间,C语言中我们学习过在内存中动态开辟空间的方式,我们通过malloc与free来进行空间开辟与销毁。
  2. 而C++中有新的动态开辟空间的方法,new与delete,它们在使用上更加方便,且可以应用于更广泛的场景,接下来,就让我们来进行对其的学习与使用。

2.1 new/delete开辟内置类型空间

new:

//开辟一个指定类型大小的空间
int* ptr1 = new int;//为开辟的空间赋于指值
int* ptr2 = new int(10);//开辟连续n个指定类型大小的空间
int* ptr3 = new int[10];//连续空间的初始化赋值
int* ptr4 = new int[3]{1,2,3};

delete:

//释放大小为1个指定类型大小的空间
delete ptr1;//释放大小为多个指定类型空间大小的空间
delete[] ptr3

注: new/delete 与 new[]/delete[] 必须配合使用,不能混用

2.2 new/delete开辟销毁自定义类型空间

  1. new/delete开辟自定义类型的动态空间,会自动调用自定义类型的构造与析构函数
class A
{
private:int _a;
public:A(int a = 0):_a(a){cout << "A()" << endl;}A(const A& tmp){_a = tmp._a;cout << "A(const A&)" << endl;}	~A(){cout << "~A()" << endl;}
};int main()
{A* pa1 = new A;delete pa1;A* pa2 = new A[3];delete[] pa2;return 0;
}
  1. 开辟自定义类型空间的初始化方式
A aa1;
A aa2;
A aa3;//一段空间
//方法1:(用存在的对象)
A* pa1 = new A(aa1);
//方法2:(创建匿名对象)
A* pa2 = new A(A());
//方法3:(隐式类型转换,构造 + 拷贝构造,优化为构造)
A* pa3 = new A(3);//多段空间
//方法1:
A* pa4 = new A[3]{aa1, aa2, aa3};
//方法2:
A* pa5 = new A[3]{A(), A(), A()};
//方法3:(前三个元素初始化为1,2,3,后面会赋值的部分会全部默认初始化为0,类似数组)
A* pa6 = new A[10]{1, 2, 3};

3. operator new 与 operator delete函数

  1. new和delete是我们进行动态内存申请和释放的操作符,而其实现空间的开辟与释放的方式为,去调用用名为operator new 和operator delete的两个函数。
  2. 这两个函数是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间,接下来就让我们来学习这两个函数的相关知识。(虽然名为operator,但与运算符重载无关,两者为全局函数)
  3. operator new与operator delete的实现,底层为直接调用malloc与free来实现空间的动态开辟,其用法也与malloc/free相同。(operator new与operator delete两者可以直接调用)
int* pa = (int*)operator new(sizeof(int));*pa = 10;
cout << *pa << endl;operator delete(pa);
  1. 既然operator new/delete实现依旧是调用malloc/free实现动态开辟空间,那为什么不去直接使用malloc/free?
  2. malloc申请空间失败会返回0(NULL),此返回值不符合面向对象的编程特性,所以C++对其进行了一层封装,使得申请空间失败后抛出异常,为了与malloc的封装匹配,于是将free也进行了封装,封装为operator delete。
  3. 调用抛出异常演示:
//连续申请空间,申请空间不足,开辟失败
void func_test()
{char* c1 = new char[1024 * 1024 * 1024];//cout << c1 << endl;//char类型的变量,流插入操作自动识别时会默认识别为字符串而不是指针cout << (void*)c1 << endl;//捕获异常的操作会改变执行流,不再执行下面操作,而会抛出异常//类似gotochar* c2 = new char[1024 * 1024 * 1024];cout << (void*)c2 << endl;
}int main()
{try{func_test();}catch (const exception& e){cout << e.what() << endl;}return 0;
}

4. new与delete的实现原理

  1. 申请开辟自定义类型空间时,new操作符会先开辟出指定大小的空间,而后调用构造函数初始化开辟出的空间。(申请空间 + 调用构造)

在这里插入图片描述

  1. delelte销毁自定义类型的申请空间时,会先调用自定义类型的析构函数,而后再进行空间的销毁释放。(调用析构,销毁空间)

在这里插入图片描述

  1. new/delete操作符在编译时会直接按照上述调用步骤,生成汇编指令。(汇编调试,call,jump)
  1. new调用new,申请时,会先开辟空间,再调用构造,构造时再调用new。销毁时,会先调用析构,析构先销毁里层new申请的空间,而后再销毁外层new申请的空间。

在这里插入图片描述

class Stack
{
private:int* _a;int _capacity;int _top;
public:Stack(int n = 4){cout << "Stack()" << endl;_a = new int[n];_top = 0;_capacity = 4;}~Stack(){cout << "~Stack()" << endl;delete[] _a;_top = 0;_capacity = 0;}
};int main()
{Stack* p1 = new Stack;delete[] p1;return 0;
}
  1. new/delete与new[]/delete[]不匹配使用可能会产生的风险与delete[]的工作原理:
    (汇编调试)
int main()
{Stack* p1 = new Stack[10];delete[] p1;return 0;
}

在这里插入图片描述

  1. 10个Stack类型的变量组成的空间大小应为120字节,可是,经过汇编调试后,大小却为124字节。这是因为,new[]申请连续的自定义类型空间时,会额外在头部申请一个四字节大小的空间用来存放申请的自定义类型空间个数,delete[]在析构时会向前调整四个字节,读取需要调用析构的次数。(delete获取需要调用析构的次数)

在这里插入图片描述

  1. 当使用delete去释放new[] 出的空间时,不会向前调整四个字节,而是调用一次析构函数后,从第一个元素的首地址释放空间,会导致内存泄漏。
  1. 编译器的优化:因为类A的成员变量只有一个int变量,且构造时没有开辟额外空间,当我们将类A的析构函数屏蔽后(没有使用析构的必要),当再次new[]一段连续空间时,将不再于头部开辟额外空间存储元素个数。delete所要做的就只是释放开辟的空间,这一点,使用free也同样可以做到。
class A
{
private:int _a;
public:A(){cout << "A()" << endl;}/*~A(){cout << "~A()" << endl;}*/
};int main()
{A* pa = new A[10];//用delete去销毁new[]申请的空间delete pa;//free(pa);return 0;
}

5. 定位new表达式与池化计数

  1. 在前面的学习中我们了解到,类的析构函数可以显示调用,而构造函数却不可以。那么,当我们不使用new的方式开辟出自定义类型的空间后,有没有办法对这段空间使用构造函数进行初始化呢,接下来,我们引入定位new表达式。
//调用默认构造
new(指针:指定空间地址)类型
//赋予初始值
new(指针)类型(初始值)

示例:

class A
{
private:int _a;
public:A(int a = 0):_a(a){cout << "A()" << endl;}~A(){cout << "~A()" << endl;}
};int main()
{A* pa1 = (A*)operator new(sizeof(A));//调用构造new(pa1)A;//析构pa1->~A();//释放空间operator delete(pa1);A* pa2 = (A*)operator new(sizeof(A));//调用构造,并指定初始值new(pa1)A(10);pa2->~A();operator delete(pa2);return 0;
}
  1. 定位new应用场景:
    <1> 因为new申请动态开辟的空间是在堆上,很多时候我们需要频繁的调用申请空间,这样的效率非常低。
    <2> 所以,C++中创建了内存池来应对这一类问题,先提前申请出一大块空间备用,这块空间被称为内存池,当我们需要申请空间时,不用再去堆上申请,而是可以直接找内存池申请划分。
    <3> 这些申请来的空间(自定义类型)是已经开辟好的,这些空间没有调用构造函数进行初始化,无法使用,而定位new就可以解决这样的问题。

6. malloc/free与new/delete的异同

相同点:

  1. 都是于堆区上开辟空间,且都需要手动释放

不同点:

  1. malloc申请的空间不会进行初始化,而new申请出的空间可以进行初始化
  2. malloc申请空间需要计算空间大小,而new只需要指定对象个数
  3. malloc的返回必须要强转为对应类型的指针,而new在申请空间时就声明了类型
  4. malloc申请空间失败返回空,new申请失败抛出异常
  5. malloc开辟销毁空间时不会调用构造,析构函数,而new/delete开辟销毁空间时会调用构造与析构

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

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

相关文章

表结构 / 字段操作

一.增 alter table emp add wickname varchar(20); 二.改 1.仅仅改变数据类型&#xff08;字段类型&#xff09; alter table emp modify wickname varchar(30); 2.字段名 和 字段类型 都改变 alter table emp change wickname username varchar(20); 三.删 alter table emp …

ZigBee技术与实训教程(无线传感网技术第五天持续更新)

ZigBee具有广阔的应用前景。 家庭和楼宇网络。工业控制。公共场所。农业控制。医疗。商业。 1.ZigBee的协议框架 Zigbee栈是在IEEE 802.15.4标准基础上建立的&#xff0c;定义了MAC层和PHY&#xff08;数据链路层&#xff09;。ZigBee设备还包括IEEE 802.15.4(该标准定义了r…

多线程锁.

公平锁与非公平锁 公平锁是指多个线程按照申请锁的顺序来获取锁,这里类似排队买票,先来的人先买后来的人在队尾排着,这是公平的Lock lock new Reentrantlock(true);/true表示公平锁,先来先得非公平锁指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请…

类和对象 (中)

文章目录 类的六个默认成员函数构造函数析构函数特性使用 总结构造函数和析构函数拷贝构造函数特性拷贝构造总结 赋值运算符的重载运算符重载赋值运算符重载总结拷贝构造函数和赋值运算符重载 关于operator<<重载日期类实现const 修饰的成员函数取地址重载以及const取地址…

css实现高度是宽度一半的效果

1、方法一&#xff1a;使用变量:root、var()、clac()实现&#xff1a; 1.1 效果如下&#xff1a; 2.2 代码如下&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title>&l…

伸手党必备之Python正则表达式常用函数

今天介绍一下Python中常用的正则表达式处理函数。Python的正则表达式主要有两种方法完成模式匹配&#xff1a;『搜索』和『匹配』 re.match re.match 尝试从字符串的开始全部或者部分匹配某个模式&#xff0c;如&#xff1a;下面的例子匹配第一个单词。 import re text &…

springboot269反欺诈平台的建设

反欺诈平台设计与实现 摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装反欺诈平台软件来发挥其高效地信息处…

Vue-Vben-Admin:中大型项目后台解决方案及如何实现页面反向传值

Vue-Vben-Admin&#xff1a;中大型项目后台解决方案及如何实现页面反向传值 摘要&#xff1a; Vue-Vben-Admin是一个基于Vue3.0、Vite、Ant-Design-Vue和TypeScript的开源项目&#xff0c;旨在为开发中大型项目提供一站式的解决方案。它涵盖了组件封装、实用工具、钩子函数、动…

学习c语言:单链表的应用

一、单链表经典算法 1.1 单链表相关经典算法OJ题1&#xff1a;移除链表元素 . - 力扣&#xff08;LeetCode&#xff09;. - 备战技术面试&#xff1f;力扣提供海量技术面试资源&#xff0c;帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。https://leetcode.…

WPF监控平台(科技大屏)[一]

跟着B站的视频敲了一个略微复杂的WPF界面,链接如下.在这里我详细的写一份博客进行设计总结. 系统介绍和配置及主窗口设计_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1Wy421Y7QD?p1&vd_source4796b18a2e4c1ec8a310391a5644b6da 成果展示 实现过程 总体来说,我的…

OpenHarmony开源项目—工程管理

DevEco Studio的基本使用&#xff0c;请参考DevEco Studio使用指南。本章主要介绍如何使用DevEco Studio进行多设备应用开发。 说明&#xff1a; 本章的内容基于DevEco Studio 3.1.1 Release版本进行介绍&#xff0c;如您使用DevEco Studio其它版本&#xff0c;可能存在文档与产…

微信小程序(五十八)分步表单多页面传值

注释很详细&#xff0c;直接上代码 新增内容&#xff1a; 1.分步表单传值 2.伪数据生成 源码&#xff1a; app.json {"pages": ["pages/index/index","pages/building/building","pages/room/room","pages/logs/logs"],&qu…

关于类和对象超级初级小白知识

下面的内容只是一小部分&#xff0c;在整个面向对象的知识中并不完整&#xff0c;用于记忆和梳理 目录 前言&#xff1a;类和对象是什么&#xff1f; 一、定义类 1.如何定义类 2.类的注意事项 二.类的实例化 1.创建对象的基础知识 2.如何创建对象 3.实例化举例 4.访问对象…

【Python】python实现决策树算法和贝叶斯算法(附源代码)

使用一种你熟悉的程序设计语言&#xff0c;实现&#xff08;1&#xff09;贝叶斯算法和&#xff08;2&#xff09;决策树算法 目录 1、贝叶斯算法2、决策树算法3、两种算法比较 1、贝叶斯算法 import pandas as pd import numpy as np from sklearn.model_selection import t…

算法-DFS/BFS-XMUOJ提瓦特细胞探秘

题目 思路 这道题目描述了一个矩阵中由数字1至9组成的细胞&#xff0c;这些细胞按照特定规则相互连接。具体规则是&#xff0c;只有当细胞上下左右紧邻的数字同样属于1至9时&#xff0c;它们才会被视为同一细胞的一部分。 解决这个问题的关键在于如何遍历整个矩阵&#xff0c…

灵雀云开源ALB ,为云原生 Kubernetes 网关领域再添新星

ALB (Another Load Balancer)是一款由灵雀云倾力打造的云原生 Kubernetes 网关&#xff0c;近日&#xff0c;正式宣布将其在Github上开源。ALB 的诞生&#xff0c;旨在满足日益增长的云原生应用需求&#xff0c;为开发者提供更为强大和灵活的网关解决方案。 ALB 支持在一个集群…

Oracle PL/SQL Programming 第9章:Numbers 读书笔记

总的目录和进度&#xff0c;请参见开始读 Oracle PL/SQL Programming 第6版 本章谈3点&#xff1a; 可使用的数字数据类型如何在数字和文本间转换PL/SQL 内置数值函数 Numeric Datatypes NUMBER&#xff1a;平台无关的实现&#xff0c;适合处理货币金额PLS_INTEGER 和 BINA…

前端面试题汇总

基础面试题 1.new 操作符做了那些事 function Fun(name){this.name name } Fun.prototype.sayHi funtion(){console.log(this.name) }function mockCreate(fn, ...args){let obj Object.create({}) // 创建一个空对象Object.setPrototypeOf(obj, fn.prototype) // 空对象…

day02vue学习

day02 一、今日学习目标 1.指令补充 指令修饰符v-bind对样式增强的操作v-model应用于其他表单元素 2.computed计算属性 基础语法计算属性vs方法计算属性的完整写法成绩案例 3.watch侦听器 基础写法完整写法 4.综合案例 &#xff08;演示&#xff09; 渲染 / 删除 / 修…

速卖通批量注册买家号安全吗?怎么弄?

在速卖通等跨境电商平台上&#xff0c;买家号的注册与养号过程繁琐而复杂。传统的手动注册方式效率低下&#xff0c;难以满足大规模的需求。而跨境智星系统凭借其全自动化的功能&#xff0c;能够轻松实现买家号的批量注册与养号&#xff0c;大大提高了效率。 使用跨境智星系统进…