首页 > 系统相关 >深入理解C/C++的内存管理

深入理解C/C++的内存管理

时间:2024-04-04 23:30:39浏览次数:23  
标签:int C++ 深入 内存 operator new delete

在C和C++中,高效的内存管理是编写性能优化和资源高效利用程序的关键。本文将深入探讨C/C++内存管理的各个方面,包括内存的分布、C语言和C++中的动态内存管理方式,以及newdelete操作符的使用

C/C++内存分布

C和C++程序的内存可以分为以下几个区域:

  • 栈(Stack):自动存储局部变量。当函数被调用时,局部变量在栈上创建,函数返回时,这些变量被销毁。
  • 堆(Heap):用于动态内存分配。与栈不同,堆上的内存分配和释放需要手动管理。
  • 全局/静态存储区:存放全局变量和静态变量。这部分内存在程序启动时分配,在程序结束时释放。
  • 文字常量区:常量字符串等常量数据存放的地方。它们在程序生命周期内不变。
  • 程序代码区:存放程序执行代码的内存区域。

 

 

理解这些区域对于避免内存泄漏和优化程序性能至关重要。

C语言中动态内存管理方式

C语言提供了几种动态内存管理的方法,包括:

  • malloc:分配指定大小的内存块。分配的内存未初始化,可能包含垃圾数据。
  • calloc:分配并初始化指定数量的内存块。初始化为零。
  • realloc:重新分配之前分配的内存块的大小。
  • free:释放之前分配的内存块。

这些函数允许程序在运行时根据需要分配和释放内存,但也需要开发者负责管理这些内存,以避免内存泄漏。

#include <stdlib.h>

void dynamicMemoryExample() {
    int* ptr = (int*)malloc(sizeof(int) * 4); // 分配内存
    if (ptr != NULL) {
        for (int i = 0; i < 4; i++) {
            ptr[i] = i; // 使用内存
        }
    }
    free(ptr); // 释放内存
}

 

C++中动态内存管理

C++提高了动态内存管理的抽象层次,通过newdelete操作符提供更为直观和安全的方式来处理内存。

  • 使用new分配内存不仅会分配内存,还会调用对象的构造函数,这提供了初始化对象的机会。
  • 使用delete释放内存时,会调用对象的析构函数,然后释放内存,这提供了清理资源的机会
#include <iostream>

void dynamicMemoryExampleInCpp() {
    int* ptr = new int(10); // 动态分配内存并初始化
    std::cout << *ptr << std::endl; // 使用内存
    delete ptr; // 释放内存,并调用析构函数
}

 

void Test()
{
  // 动态申请一个int类型的空间
  int* ptr4 = new int;
  
  // 动态申请一个int类型的空间并初始化为10
  int* ptr5 = new int(10);
  
  // 动态申请10个int类型的空间
  int* ptr6 = new int[3];
  delete ptr4;
  delete ptr5;
  delete[] ptr6;
}

 

注意:申请和释放单个元素的空间,使用 new delete 操作符,申请和释放连续的空间,使用 new[] delete[] ,注意:匹配起来使用。

 

newdelete操作自定义类型 

class A
{
public:
 A(int a = 0)
 : _a(a)
 {
 cout << "A():" << this << endl;}
 ~A()
 {
 cout << "~A():" << this << endl;
 }
private:
 int _a;
};
int main()
{
 // new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间
还会调用构造函数和析构函数
 A* p1 = (A*)malloc(sizeof(A));
 A* p2 = new A(1);
 free(p1);
 delete p2;
 // 内置类型是几乎是一样的
 int* p3 = (int*)malloc(sizeof(int)); // C
 int* p4 = new int;
free(p3);
delete p4;
 A* p5 = (A*)malloc(sizeof(A)*10);
 A* p6 = new A[10];
 free(p5);
 delete[] p6;
 return 0;
}
注意:在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc与 free不会。

 

operator new与operator delete函数

在C++中,newdelete不仅仅是操作符,它们背后实际上是调用了operator newoperator delete函数。这两个函数负责分配和释放动态内存。重要的是,C++允许开发者重载这些函数来提供自定义的内存管理策略。

自定义内存管理

自定义内存管理通常用于优化性能,例如通过实现特定的内存池来避免频繁地向操作系统请求小块内存。这样可以显著减少内存碎片以及提高内存分配和释放的效率。

#include <iostream>
#include <cstdlib>

void* operator new(size_t size) {
    std::cout << "Custom new for size: " << size << std::endl;
    return malloc(size);
}

void operator delete(void* memory) noexcept {
    std::cout << "Custom delete" << std::endl;
    free(memory);
}

通过这个简单的例子,我们看到了如何拦截所有的newdelete调用,以便在分配和释放内存时执行自定义逻辑

new和delete的实现原理

new操作符在底层调用operator new函数来分配内存,并在成功分配内存后调用对象的构造函数。相似地,delete操作符首先调用对象的析构函数,然后调用operator delete函数来释放内存。

这种分离使得newdelete不仅仅关注内存的分配和释放,还能确保对象生命周期的正确管理。

如果申请的是内置类型的空间, new 和 malloc , delete 和 free 基本类似,不同的地方是: new/delete 申请和释放的是单个元素的空间, new[] 和 delete[] 申请的是连续空间,而且 new 在申 请空间失败时会抛异常, malloc 会返回 NULL 自定义类型 new 的原理
1. 调用 operator new 函数申请空间 2. 在申请的空间上执行构造函数,完成对象的构造
delete 的原理
1. 在空间上执行析构函数,完成对象中资源的清理工作 2. 调用 operator delete 函数释放对象的空间
new T[N] 的原理
1. 调用 operator new[] 函数,在 operator new[] 中实际调用 operator new 函数完成 N 个对 象空间的申请 2. 在申请的空间上执行 N 次构造函数
delete[] 的原理
1. 在释放的对象空间上执行 N 次析构函数,完成 N 个对象中资源的清理 2. 调用 operator delete[] 释放空间,实际在 operator delete[] 中调用 operator delete 来释 放空间

 

也就是说当调用delete[]的时候,地址的第一个字节处存放的是n,也就是需要调用n次析构,如果我们使用的是delete,默认从当前位置开始析构,那么位置就会出错。 

 

定位new表达式(placement-new)

定位new表达式是C++中的一个高级特性,允许在已分配的内存上构造对象。这对于内存池、缓存管理等场景非常有用,因为它允许重复使用已分配的内存来创建新对象,避免了频繁的内存分配和释放操作。

#include <new> // 必须包含这个头文件

char buffer[1024]; // 预分配内存

void placementNewExample() {
    int* p = new (buffer) int(10); // 在buffer上构造int对象
    // 使用p...
}

 

使用定位new表达式时,需要确保操作的内存足够大且正确对齐,以容纳指定类型的对象。

常见面试题

内存区域划分

问:请描述C/C++程序中的内存区域划分。

答:C/C++程序的内存分为栈、堆、全局/静态存储区、文字常量区和程序代码区。

动态内存管理

问:mallocnew的区别是什么?

答:malloc仅分配内存,不调用构造函数;new分配内存的同时调用构造函数初始化对象。相应地,freedelete的区别在于delete会先调用析构函数再释放内存。

定位new

问:什么是定位new表达式,它有什么用途?

答:定位new表达式允许在已分配的内存上构造对象。这在需要在特定位置创建对象,或者在内存池、缓存管理等场景下重复使用内存时非常有用。


通过本文,我们深入探讨了C/C++中的内存管理,从基本的内存区域划分到高级特性如定位new表达式,以及如何通过重载operator newoperator delete来实现自定义内存管理策略。理解这些概念不仅对于写出高性能的C/C++代码至关重要,也是面试中常见的问题。希望这篇博客能帮助你在C/C++内存管理方面达到新的高度!

标签:int,C++,深入,内存,operator,new,delete
From: https://blog.csdn.net/2301_77754590/article/details/137383176

相关文章

  • 我的C++奇迹之旅:值和引用的本质效率与性能比较
    文章目录......
  • 小猫爬山 C++题解
    小猫爬山内存限制:256MiB时间限制:1000ms标准输入输出题目类型:传统评测方式:文本比较题目描述Freda和rainbow饲养了N只小猫,这天,小猫们要去爬山。经历了千辛万苦,小猫们终于爬上了山顶,但是疲倦的它们再也不想徒步走下山了(呜咕>_<)。Freda和rainbow只好花钱让它......
  • 小木棍 C++题解
    小木棍内存限制:1024MiB时间限制:1000ms标准输入输出题目类型:传统评测方式:文本比较题目描述乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50。现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。给出每......
  • C++之类
    目录一:面向过程和面向对象的初步认识二:类的引入三:类的定义3.1类的两种定义方式:3.2成员变量命名的建议四:类的访问限定符及封装4.1类的访问限定符4.2封装一:面向过程和面向对象的初步认识C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题......
  • 第十四届蓝桥杯B组c/c++第五题接龙数列
    动态规划  接龙数列我打眼一看感觉得用栈stack,取出首位和末位全都入栈,每次弹出栈顶,获取此时的栈顶并弹出和下一个栈顶比较。整了老半天发现不行,原来是我脑子瓦特了。虽然没有用栈解决这道问题,但是,栈和队列都是非常重要的只是,不了解的同学们可以去学习一下,下面有传送门。......
  • C++内存管理
    前言:本篇将介绍c/c++的内存空间结构与c++中对内存进行管理的用法,包括new,delete,operatornew与operatordelete,定位new以及与c中malloc和free的区别等,到stl容器的底层实现篇将会对内存操作进行模拟实现,会进一步加深对内存管理的理解。目录前言:1.new与delete操作符2.c/c++......
  • C++ 实验 03
    实验3.1设计一个用来表示直角坐标系的Location类,有两个double型私有数据成员x,y;主程序中,输入相应的值,创建类Location的两个对象a和b,分别采用成员函数和友元函数计算给定两个坐标点之间的距离。【提示】类Location的参考框架如下:classLocation{public:       Loc......
  • 【c++初阶】类与对象(下)
    ✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨......
  • Qt C++ | Qt 元对象系统、信号和槽及事件(第一集)
    01元对象系统一、元对象系统基本概念1、Qt的元对象系统提供的功能有:对象间通信的信号和槽机制、运行时类型信息和动态属性系统等。2、元对象系统是Qt对原有的C++进行的一些扩展,主要是为实现信号和槽机制而引入的,信号和槽机制是Qt的核心特征。3、要使用元对象系统......
  • 《C++程序设计》阅读笔记【2-程序结构】
    ......