(注:该文章只讲解了C++手动管理内存的方式,自动管理方式也就是智能指针在新特性栏目中,感兴趣的小伙伴可以去了解,非常感谢)

目录

一、了解内存分区

1、栈(Stack)

2、堆(Heap)

3、全局 / 静态存储区(Global/Static Storage)

4、常量存储区(Constant Storage)

5、代码区(Code Segment)

二、C语言风格的内存管理:

malloc函数

free函数

示例:

注意事项:

三、C++的内存管理:

new操作符

delete操作符

示例:

四、malloc/free与new/delete的核心差异

五、常见错误与注意事项

1. 混用malloc与delete

2. 混用new与free

3. new[]与delete不匹配

4. 重复释放或释放非动态内存

六、使用场景建议

七、总结


C++ 提供了多种内存管理方式,包括传统的 C 风格的 mallocfree,以及 C++ 专用的 newdelete

理解这些内存管理方法对于编写高效、安全的 C++ 程序至关重要。

一、❤️了解内存分区

C++ 程序运行时的内存主要分为以下几个区域,各自有不同的生命周期和用途:

1、✨栈(Stack)
  • 特性自动管理内存,存储局部变量和函数调用信息。内存分配和释放速度快,但空间有限
  • 存储内容:函数参数、局部变量、返回地址等
  • 大小:通常较小,由系统自动管理
  • 示例:
    void func() {
        int a = 10;  // 存储在栈上,函数结束后自动释放
        double b;    // 栈上分配
    }
2、✨堆(Heap)
  • 特性动态分配与释放,需要程序员手动管理(或通过智能指针自动管理),灵活但易出错(如内存泄漏、悬挂指针)。
  • 存储内容:程序运行时动态创建的对象 / 数据
  • 大小:较大,生命周期由开发者控制
  • 示例:
    int* p = new int(5);  // 堆上分配int
    delete p;             // 手动释放
3、✨全局 / 静态存储区(Global/Static Storage)
  • 特性程序启动时分配,程序结束时释放
  • 存储内容:全局变量、静态变量(static修饰)
  • 示例:
    int global_value = 20;       // 全局变量
    void func() {
        static int static_value; // 静态局部变量
    }

4、✨常量存储区(Constant Storage)
  • 特性存储常量,不可修改,程序结束时释放
  • 示例:
    const int MAX = 100;  // 常量
    char* str = "hello";  // "hello"存储在常量区
5、✨代码区(Code Segment)
  • 特性存储程序的二进制指令,只读
  • 作用:确保程序指令不会被意外修改

二、❤️C语言风格的内存管理:
✨malloc函数

malloc(memory allocation)用于在堆上分配指定字节数的内存

其函数原型:

void * malloc(size_t size);
  • 参数size 指要分配的内存字节数。
  • 返回值:指向分配内存的指针,如果分配失败则返回 nullptr
  • 功能:从堆中分配一块连续的、大小为size字节的内存块。
  • 特点:
    • 只负责分配内存,不初始化内存
    • 返回void*类型,需显式转换为目标指针类型。
✨free函数

free 用于释放之前由 malloccallocrealloc 分配的内存

其函数原型:

void free(void* ptr);
  • 参数ptr 指要释放的内存指针。
  • 功能:释放malloc(或calloc/realloc)分配的内存块,归还给系统。
  • 特点:
    • 只释放内存,不销毁对象(因为C 语言中无 “对象” 概念,仅仅释放原始内存)。
    • 释放后指针变为 “悬空指针”,建议置为nullptr避免误用
    • 不能释放非malloc分配的内存(如栈内存),否则会导致未定义行为。
  • 其他配套函数:
    • calloc(size_t num, size_t size):分配num个大小为size的内存块,并初始化为 0。
    • realloc(void* ptr, size_t size):调整已分配内存块的大小,可能移动内存位置。
示例:
#include <iostream>
#include <cstdlib> // 包含 malloc 和 free

int main() {
    // 分配一个整数的内存
    int* p = (int*)malloc(sizeof(int));
    if (p == nullptr) {
        std::cerr << "memory allocation failed" << std::endl;
        return 1;
    }

    *p = 42;
    std::cout << "value: " << *p << std::endl;

    // 释放内存
    free(p);
    p = nullptr; // 避免悬空指针
    return 0;
}
注意事项:
  • 类型转换malloc 分配内存成功,返回 void*,所以需要显式转换为所需类型的指针。
  • 初始化malloc 分配的内存未初始化,内容是不确定的。
  • 释放必须对应:由 malloc 分配的内存必须使用 free 释放,而不是使用 delete

三、❤️C++的内存管理:

C++ 在 C 的基础上扩展了内存管理,new/delete不仅管理内存,还与对象的生命周期绑定(调用构造 / 析构函数)

✨new操作符
  • 功能从堆中分配内存,并自动调用对象的构造函数初始化
  • 特点
    • 无需显式指定内存大小(由对象类型自动推导)。
    • 无需类型转换(返回对应类型的指针)。
    • 分配失败时抛出std::bad_alloc异常(默认行为),或返回nullptr(但是需指定nothrow)。
    • 支持数组分配(new[])和定位new(在已分配内存中构造对象)。
✨delete操作符
  • 功能用于释放由 new 分配的内存,先调用对象的析构函数销毁对象,再释放内存
  • 特点
    • 必须与new配对使用(delete[]new[]配对)
    • 释放数组时必须用delete[],否则只会调用第一个元素的析构函数,导致内存泄漏。
    • 释放后指针变为悬空指针,建议置为nullptr
示例:
#include <iostream>

class Dog {
public:
	Dog() {
		std::cout << "have a dog..." << std::endl;
	}

	~Dog() {
		std::cout << "dog is died..." << std::endl;
	}
};

int main() {
	// 分配单个对象:内存分配 + 构造函数调用
	int* num = new int(10);  // 分配int并初始化为10
	Dog* dog = new Dog();    // 分配Dog对象并调用其构造函数

	// 分配数组:内存分配 + 每个元素的构造函数调用
	int* arr = new int[5] {1, 2, 3, 4, 5};  // C++11起支持初始化列表
	Dog* dogs = new Dog[3];   // 调用了3次Dog构造函数

	// 不抛异常的分配方式
	int* p = new (std::nothrow) int;  // 失败时返回nullptr

	// 释放单个对象:析构函数调用 + 内存释放
	delete num;   // 对于基础类型,仅释放内存(没有析构函数)
	delete dog;   // 调用Dog析构函数,再释放内存

	// 释放数组:每个元素析构 + 内存释放
	delete[] arr;   // 释放int数组(基础类型无析构)
	delete[] dogs;  // 调用3次Dog析构函数,再释放内存

	num = nullptr;  // 避免悬空指针
}

输出:

have a dog...
have a dog...
have a dog...
have a dog...
dog is died...
dog is died...
dog is died...
dog is died...

四、❤️malloc/freenew/delete的核心差异

维度

malloc/free(C)

new/delete(C++)

操作对象

原始内存块(无 “对象” 概念)

对象(内存分配 + 构造 / 析构)

初始化

不初始化内存(内容随机)

自动调用构造函数初始化(基础类型可显式初始化)

类型处理

返回void*,需显式转换

自动匹配类型,无需转换

大小指定

需手动传入字节数

(如sizeof(int)

由类型自动推导,无需指定

异常处理

失败返回nullptr

失败默认抛出std::bad_alloc

,可指定nothrow抛出nullptr

数组支持

需手动计算总大小

n * sizeof(T)

直接用new[],自动计算大小

与类的交互

不调用构造 / 析构函数(无法管理对象生命周期)

自动调用构造 / 析构函数(核心差异)

重载支持

不能重载

可以重载(自定义内存分配逻辑)

五、❌常见错误与注意事项
1.🥀 混用mallocdelete

❌错误示例:

int* p = (int*)malloc(sizeof(int));
delete p;  // 错误:malloc分配的内存不能用delete释放

原因:malloc仅分配内存,delete会尝试调用析构函数(对基础类型无影响,但对类对象会出错)。

2. 🥀混用newfree

❌错误示例:

Dog* dog = new Dog();
free(dog);  // 错误:new分配的内存不能用free释放

原因:free不会调用析构函数,导致对象资源未释放(内存泄漏)。

3. 🥀new[]delete不匹配

❌错误示例:

Dog* dogs = new Dog[3];
delete dogs;  // 错误:应使用delete[]

后果:只调用第一个Dog的析构函数,后两个对象的资源泄漏,且内存释放可能异常

4. 🥀重复释放或释放非动态内存
  • 对同一指针多次free/delete会导致堆损坏
  • 释放栈内存(如int a; free(&a);)会导致程序崩溃。

六、❤️使用场景建议
  • C 语言:只能使用malloc/free(或calloc/realloc)。
  • C++ 语言
    • 优先使用new/delete(而非malloc/free),尤其是管理类对象时(需要构造 / 析构)。
    • 更推荐使用智能指针(std::unique_ptr/std::shared_ptr)和标准库容器(std::vector),避免手动调用new/delete
    • 需兼容 C 代码时,可混用mallocfree,但注意不要与new/delete交叉使用。

七、❤️总结
  • malloc/free是 C 语言的底层内存管理工具,仅负责内存的分配与释放,不涉及对象生命周期。
  • new/delete是 C++ 的高级工具,不仅管理内存,还自动调用构造 / 析构函数,是面向对象编程的核心机制。
  • 两者不可混用,否则会导致内存泄漏、程序崩溃等未定义行为。现代 C++ 开发中,应尽量避免直接使用new/delete,转而依赖智能指针等自动管理机制。
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐