椋鸟C++笔记#5:C++内存管理

2024-06-11 07:36
文章标签 c++ 内存 笔记 管理 椋鸟

本文主要是介绍椋鸟C++笔记#5:C++内存管理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

      • C语言中的动态内存管理
      • C\+\+中的动态内存管理
        • 使用new/delete操作内置类型
        • 使用new/delete操作自定义类型
        • operator new(operator new[])与operator delete(operator delete[])函数
          • operator new函数
          • operator delete函数
          • operator new[]和operator delete[]
        • new与delete的实现原理
        • 定位new表达式(placement-new)
      • 检测内存泄露

萌新的学习笔记,写错了恳请斧正。

C语言中的动态内存管理

C语言中,我们使用malloccallocreallocfree来动态管理内存。

int main()
{int* p = (int*)malloc(sizeof(int));	//动态开辟free(p);int* q = (int*)calloc(4, sizeof(int));	//动态开辟并赋值int* r = (int*)realloc(q, 10 * sizeof(int));	//重新分配空间free(r);
}

C++中的动态内存管理

C++中有更简单方便的内存管理方式,那就是使用newdelete来管理内存。

使用new/delete操作内置类型

注意:如果使用new开辟空间就用delete删除,如果使用new[]开辟数组就用delete[]删除。

  1. 动态申请与删除一个内置类型变量的空间:

    int* pi = new int;
    int* pf = new float;
    int* pl = new long;
    int* pb = new bool;delete pi;
    delete pf;
    delete pl;
    delete pb;
    
  2. 动态申请与删除一个内置类型变量的空间并完成初始化:

    int* pi = new int(114514);
    int* pf = new float(3.14f);
    int* pl = new long(1145141919810);
    int* pb = new bool(true);delete pi;
    delete pf;
    delete pl;
    delete pb;
    
  3. 动态申请与删除内置类型变量数组的空间:

    int* pi = new int[5];
    int* pf = new float[5];
    int* pl = new long[5];
    int* pb = new bool[5];delete[] pi;
    delete[] pf;
    delete[] pl;
    delete[] pb;
    
  4. 动态申请与删除内置类型变量数组的空间并完成初始化:

    int* pi = new int[5]{1, 2, 3, 4, 5};
    float* pf = new float[5]{1.1f, 2.2f, 3.3f, 4.4f, 5.5f};
    long* pl = new long[5]{10, 20, 30, 40, 50};
    bool* pb = new bool[5]{true, false, true, false, true};delete[] pi;
    delete[] pf;
    delete[] pl;
    delete[] pb;
    
使用new/delete操作自定义类型

这与自定义类型区别主要在于:使用new/delete操作自定义类型时会自动调用自定义类型的构造/析构函数。

class A
{
public:A(int a = 0): m_a(a){cout << "A" << endl;}~A(){cout << "~A" << endl;}private:int m_a;
};int main()
{A* oA = new A;delete oA;A* oB = (A*)malloc(sizeof(A));free(oB);return 0;
}

该程序运行结果为:

A
~A
operator new(operator new[])与operator delete(operator delete[])函数

看到operator new和operator delete,很多人会以为这是new与delete的重载函数。

但是,operator new和operator delete不能理解为是new与delete的重载函数!

operator new与operator delete其实是系统提供的全局函数,而new和delete在底层其实就是调用了这两个函数来实现的。

以下给出的函数定义随编译器不同有所变化!

operator new函数
void* operator new(std::size_t size) 
{if (size == 0)	// 如果请求的大小为0,分配至少一个字节{size = 1;}while (true){void* p = std::malloc(size);  // 使用malloc分配内存if (p) {return p;  // 如果分配成功,返回指针}std::new_handler handler = std::get_new_handler();  // 获取当前的new_handlerif (!handler) {throw std::bad_alloc();  // 如果没有设置new_handler,抛出bad_alloc异常}handler();  // 调用new_handler}
}
operator delete函数
void operator delete(void* ptr) noexcept	//noexcept说明此函数不会抛出异常
{std::free(ptr);  // 使用free释放内存
}
operator new[]和operator delete[]

同样的,new[]和delete[]在底层调用的是operator new[]和operator delete[]函数。

void* operator new[](std::size_t size) 
{if (size == 0) {size = 1;}while (true) {void* p = std::malloc(size);if (p) {return p;}std::new_handler handler = std::get_new_handler();if (!handler) {throw std::bad_alloc();}handler();}
}void operator delete[](void* ptr) noexcept 
{std::free(ptr);
}
new与delete的实现原理

如果申请的是内置类型的空间,new和malloc、delete和free基本类似。

不同的是:

  1. new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间
  2. new在申请空间失败时会抛异常;malloc会返回NULL。

对于自定义类型:

  1. new的原理:
    • 调用 operator new 函数申请空间。
    • 执行构造函数。
  2. delete的原理:
    • 执行析构函数。
    • 调用 operator delete 函数释放空间。
  3. new[]的原理:
    • 调用 operator new[] 函数申请空间。
    • 分别执行构造函数。
    • 在这片空间前紧挨的位置申请4个字节用于保存N。
  4. delete[]的原理:
    • 往前读取4个字节,获得N。
    • N个元素分别执行析构函数。
    • 调用 operator delete[] 函数释放空间。
定位new表达式(placement-new)

我们可以直接通过指针显式的调用析构函数:

#include <iostream>using namespace std;class A
{
public:A(int a = 0): m_a(a){cout << "A()" << this << endl;}~A(){cout << "~A()" << this << endl;}private:int m_a;
};int main()
{A* p1 = new A;p1->~A();free(p1);return 0;
}

那我们能直接显式调用构造函数吗?答案是否定的。

但是我们可以使用定位new表达式

#include <iostream>using namespace std;class A
{
public:A(int a = 0): m_a(a){cout << "A()" << this << endl;}~A(){cout << "~A()" << this << endl;}private:int m_a;
};int main()
{A* p1 = (A*)malloc(sizeof(A));new (p1) A;	//在地址p1处执行类A的构造函数p1->~A();free(p1);return 0;
}

其结构就是new加上(这里放地址)加上类名

或者也可以同时初始化:

int main()
{A* p1 = (A*)malloc(sizeof(A));new (p1) A (10);	//在地址p1处执行类A的构造函数并初始化为10p1->~A();free(p1);return 0;
}

当然我们也能显示的调用operator new(operator new[])与operator delete(operator delete[])函数:

#include <iostream>using namespace std;class A
{
public:A(int a = 0): m_a(a){cout << "A()" << this << endl;}~A(){cout << "~A()" << this << endl;}private:int m_a;
};int main()
{A* p1 = (A*)malloc(sizeof(A));new (p1) A;p1->~A();free(p1);A* p2 = (A*)operator new(sizeof(A));new (p2) A(10);p2->~A();operator delete(p2);return 0;
}

检测内存泄露

在 Visual Stodio 中我们可以使用_CrtDumpMemoryLeaks()函数来检测内存泄露,如果发生内存泄露这个函数会在输出窗口给出提示。

#include <iostream>using namespace std;int main()
{int* p = new int;_CrtDumpMemoryLeaks();return 0;
}

执行这段代码,我们可以在来源为调试的输出看到如下内容:

Detected memory leaks!
Dumping objects ->
{81} normal block at 0x00000221287C7400, 4 bytes long.Data: <    > CD CD CD CD 
Object dump complete.

这篇关于椋鸟C++笔记#5:C++内存管理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1050539

相关文章

C++统计函数执行时间的最佳实践

《C++统计函数执行时间的最佳实践》在软件开发过程中,性能分析是优化程序的重要环节,了解函数的执行时间分布对于识别性能瓶颈至关重要,本文将分享一个C++函数执行时间统计工具,希望对大家有所帮助... 目录前言工具特性核心设计1. 数据结构设计2. 单例模式管理器3. RAII自动计时使用方法基本用法高级用法

SpringBoot 多环境开发实战(从配置、管理与控制)

《SpringBoot多环境开发实战(从配置、管理与控制)》本文详解SpringBoot多环境配置,涵盖单文件YAML、多文件模式、MavenProfile分组及激活策略,通过优先级控制灵活切换环境... 目录一、多环境开发基础(单文件 YAML 版)(一)配置原理与优势(二)实操示例二、多环境开发多文件版

Redis实现高效内存管理的示例代码

《Redis实现高效内存管理的示例代码》Redis内存管理是其核心功能之一,为了高效地利用内存,Redis采用了多种技术和策略,如优化的数据结构、内存分配策略、内存回收、数据压缩等,下面就来详细的介绍... 目录1. 内存分配策略jemalloc 的使用2. 数据压缩和编码ziplist示例代码3. 优化的

SpringBoot集成XXL-JOB实现任务管理全流程

《SpringBoot集成XXL-JOB实现任务管理全流程》XXL-JOB是一款轻量级分布式任务调度平台,功能丰富、界面简洁、易于扩展,本文介绍如何通过SpringBoot项目,使用RestTempl... 目录一、前言二、项目结构简述三、Maven 依赖四、Controller 代码详解五、Service

深入解析C++ 中std::map内存管理

《深入解析C++中std::map内存管理》文章详解C++std::map内存管理,指出clear()仅删除元素可能不释放底层内存,建议用swap()与空map交换以彻底释放,针对指针类型需手动de... 目录1️、基本清空std::map2️、使用 swap 彻底释放内存3️、map 中存储指针类型的对象

Python内存优化的实战技巧分享

《Python内存优化的实战技巧分享》Python作为一门解释型语言,虽然在开发效率上有着显著优势,但在执行效率方面往往被诟病,然而,通过合理的内存优化策略,我们可以让Python程序的运行速度提升3... 目录前言python内存管理机制引用计数机制垃圾回收机制内存泄漏的常见原因1. 循环引用2. 全局变

Linux系统管理与进程任务管理方式

《Linux系统管理与进程任务管理方式》本文系统讲解Linux管理核心技能,涵盖引导流程、服务控制(Systemd与GRUB2)、进程管理(前台/后台运行、工具使用)、计划任务(at/cron)及常用... 目录引言一、linux系统引导过程与服务控制1.1 系统引导的五个关键阶段1.2 GRUB2的进化优

C++ STL-string类底层实现过程

《C++STL-string类底层实现过程》本文实现了一个简易的string类,涵盖动态数组存储、深拷贝机制、迭代器支持、容量调整、字符串修改、运算符重载等功能,模拟标准string核心特性,重点强... 目录实现框架一、默认成员函数1.默认构造函数2.构造函数3.拷贝构造函数(重点)4.赋值运算符重载函数

Spring Security 前后端分离场景下的会话并发管理

《SpringSecurity前后端分离场景下的会话并发管理》本文介绍了在前后端分离架构下实现SpringSecurity会话并发管理的问题,传统Web开发中只需简单配置sessionManage... 目录背景分析传统 web 开发中的 sessionManagement 入口ConcurrentSess

C++ vector越界问题的完整解决方案

《C++vector越界问题的完整解决方案》在C++开发中,std::vector作为最常用的动态数组容器,其便捷性与性能优势使其成为处理可变长度数据的首选,然而,数组越界访问始终是威胁程序稳定性的... 目录引言一、vector越界的底层原理与危害1.1 越界访问的本质原因1.2 越界访问的实际危害二、基