【C++】内存管理

💗个人主页💗

⭐个人专栏------C++学习

💫点击关注🤩一起学习C语言💯💫

目录

[1. C/C++内存分布](#1. C/C++内存分布)

[2. C语言中动态内存管理方式](#2. C语言中动态内存管理方式)

[3. C++内存管理方式](#3. C++内存管理方式)

[3.1 new/delete操作内置类型](#3.1 new/delete操作内置类型)

[3.2 new和delete操作自定义类型](#3.2 new和delete操作自定义类型)

[4. operator new与operator delete函数](#4. operator new与operator delete函数)


1. C/C++内存分布

C/C++程序的内存分布主要分为以下几个部分:

  1. 栈(Stack):栈是用来存储局部变量、函数参数等的内存区域。每当一个函数被调用时,都会分配一块栈帧来存储函数的局部变量和参数。栈是按照"先进后出"的原则进行管理,函数返回后,其所占用的栈帧会被释放。

  2. 堆(Heap):堆是用来动态分配内存的区域。在C/C++中,使用new/delete或malloc/free来进行堆内存的动态分配与释放。堆是按照"先进先出"的原则进行管理,需要手动释放分配的内存。

  3. 全局/静态存储区(Global/Static Storage):全局变量和静态变量都存储在这个区域中。全局变量在程序运行期间一直存在,静态变量的生命周期是整个程序运行期间。

  4. 常量存储区(Constant Storage):存储常量数据,例如字符串常量。这个区域的数据在程序运行期间不能被修改。

  5. 代码区(Code/Text):存储程序的机器指令。代码区是只读的,程序在运行时不能修改代码区的内容。

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

在C语言中,动态内存管理主要通过以下几个函数来实现:

  1. malloc():用于分配指定大小的字节内存块。它接受一个参数,即所需内存的字节数,并返回一个指向分配内存的指针。

  2. calloc():用于分配指定数量和大小的连续内存块,并将每个字节都初始化为零。它接受两个参数,即所需内存块的数量和每个内存块的字节数,并返回一个指向分配内存的指针。

  3. realloc():用于重新分配已分配内存块的大小。它接受两个参数,即指向要重新分配大小的内存块的指针和新的内存块大小,并返回一个指向重新分配内存的指针。

  4. free():用于释放之前通过malloc()或calloc()分配的内存块。它接受一个参数,即要释放的内存块的指针。

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

int main() {
    int* numbers;
    
    // 分配存储5个整数的内存块
    numbers = (int*)malloc(5 * sizeof(int));
    if (numbers == NULL) {
        printf("内存分配失败\n");
        return 1;
    }
    
    // 向每个元素赋值
    for (int i = 0; i < 5; i++) {
        numbers[i] = i + 1;
    }
    
    // 打印每个元素的值
    for (int i = 0; i < 5; i++) {
        printf("%d ", numbers[i]);
    }
    printf("\n");
    
    // 重新分配内存块的大小为10个整数
    numbers = (int*)realloc(numbers, 10 * sizeof(int));
    if (numbers == NULL) {
        printf("内存重新分配失败\n");
        return 1;
    }
    
    // 向新增的元素赋值
    for (int i = 5; i < 10; i++) {
        numbers[i] = i + 1;
    }
    
    // 打印每个元素的值
    for (int i = 0; i < 10; i++) {
        printf("%d ", numbers[i]);
    }
    printf("\n");
    
    // 释放内存块
    free(numbers);
    
    return 0;
}

3. C++内存管理方式

C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因 此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。

  1. new/delete运算符:new运算符用于动态分配单个对象的内存,并返回指向该对象的指针,而delete运算符用于释放通过new分配的内存。相较于malloc/free函数,new和delete可以自动调用对象的构造函数和析构函数。

  2. new[]/delete[]运算符:new[]运算符用于动态分配对象数组的内存,并返回指向该数组的指针,而delete[]运算符用于释放通过new[]分配的内存。相较于malloc/free函数,new[]和delete[]可以自动调用每个对象的构造函数和析构函数。

3.1 new/delete操作内置类型

  1. new操作符:使用new操作符可以动态分配内置类型的内存空间,并返回指向该内存空间的指针。
cpp 复制代码
int* p = new int; // 动态分配一个int类型的内存空间
float* q = new float; // 动态分配一个float类型的内存空间
  1. delete操作符:使用delete操作符可以释放通过new操作符分配的内存空间,同时调用内置类型的析构函数进行资源的清理。
cpp 复制代码
delete p; // 释放之前分配的int类型的内存空间
delete q; // 释放之前分配的float类型的内存空间

需要注意的是,对于内置类型,因为它们没有构造函数和析构函数,所以delete操作符只会释放内存空间,并不会进行其他操作。

而且,使用delete操作符释放的内存空间必须是通过new操作符进行分配的,否则可能导致未定义的行为。另外,动态分配的内置类型的内存空间一旦不再需要,应当及时释放,以避免内存泄漏和资源的浪费。

cpp 复制代码
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;
}

3.2 new和delete操作自定义类型

cpp 复制代码
class MyClass 
{
public:
    int* data;

    MyClass() 
    {
        data = new int[5]; // 动态分配一个包含5个int元素的数组
        cout << "Constructor called" << endl;
    }

    ~MyClass() 
    {
        delete[] data; // 释放之前分配的数组内存空间
        cout << "Destructor called" << endl;
    }
};

int main() 
{
    MyClass* obj = new MyClass(); // 动态分配一个MyClass对象的内存空间,并进行初始化

    // 使用动态分配的内存空间
    for (int i = 0; i < 5; i++) 
    {
        obj->data[i] = i;
        cout << obj->data[i] << " ";
    }
    cout << endl;

    delete obj; // 释放之前分配的MyClass对象的内存空间,并调用析构函数

    return 0;
}

4. operator new与operator delete函数

operator new和operator delete是C++中的全局函数,用于自定义类对象的动态内存管理。new在底层调用operator new全局函数来申请空间,delete在底层通过 operator delete全局函数来释放空间。

注意: operator new和operator delete不是对new和delete的重载,这是俩库函数

operator new函数用于分配内存空间:

cpp 复制代码
void* operator new (std::size_t size);

它接受一个参数size,表示要分配的内存空间的大小,返回一个void指针,指向已分配的内存空间的起始地址。

operator delete函数用于释放内存空间:

cpp 复制代码
void operator delete (void* ptr);

它接受一个参数ptr,表示要释放的内存空间的起始地址,无返回值。

这两个函数可以被重载,用于自定义内存分配和释放的行为。

cpp 复制代码
class MyClass 
{
public:
    int data;

    MyClass(int d) : data(d) 
    {
        std::cout << "Constructor called" << std::endl;
    }

    ~MyClass() 
    {
        std::cout << "Destructor called" << std::endl;
    }

    static void* operator new (size_t size) 
    {
        std::cout << "Custom new operator called" << std::endl;
        void* ptr = std::malloc(size); // 使用malloc来分配内存空间
        if (!ptr) 
        {
            throw std::bad_alloc(); // 内存分配失败,抛出异常
        }
        return ptr;
    }

    static void operator delete (void* ptr) 
    {
        std::cout << "Custom delete operator called" << std::endl;
        std::free(ptr); // 使用free来释放内存空间
    }
};

int main() 
{
    MyClass* obj = new MyClass(10);
    delete obj;

    return 0;
}
相关推荐
arong_xu8 分钟前
现代C++锁介绍
c++·多线程·mutex
汤姆和杰瑞在瑞士吃糯米粑粑13 分钟前
【C++学习篇】AVL树
开发语言·c++·学习
Biomamba生信基地20 分钟前
R语言基础| 功效分析
开发语言·python·r语言·医药
DARLING Zero two♡21 分钟前
【优选算法】Pointer-Slice:双指针的算法切片(下)
java·数据结构·c++·算法·leetcode
手可摘星河22 分钟前
php中 cli和cgi的区别
开发语言·php
CodeClimb35 分钟前
【华为OD-E卷-木板 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
CT随1 小时前
Redis内存碎片详解
java·开发语言
anlog1 小时前
C#在自定义事件里传递数据
开发语言·c#·自定义事件
奶香臭豆腐1 小时前
C++ —— 模板类具体化
开发语言·c++·学习
游是水里的游1 小时前
【算法day19】回溯:分割与子集问题
算法