【人工智能教程】,前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。
点击跳转到网站:【人工智能教程】
文章目录
一、动态内存1. 使用`new`和`delete`①分配单个对象②分配对象数组 2. 注意事项3. 智能指针4.示例:使用`std::unique_ptr` 二、内存管理函数1. C语言风格的内存管理函数2. C++特有的内存管理操作符3. 智能指针 三、栈内存和堆内存1. 栈内存(Stack Memory)2. 堆内存(Heap Memory)3. 注意事项4. 结论 四、相关链接
一、动态内存
在C++中,动态内存管理是一个重要的概念,它允许程序在运行时根据需要分配和释放内存。这与静态内存分配(在编译时确定大小)和栈内存分配(在函数调用时自动分配和释放)不同。动态内存分配通常使用new
和delete
操作符(对于单个对象)或new[]
和delete[]
操作符(对于对象数组)来完成。
1. 使用new
和delete
①分配单个对象
int* ptr = new int; // 分配一个int类型的内存空间*ptr = 10; // 使用该内存空间delete ptr; // 释放内存ptr = nullptr; // 避免野指针
②分配对象数组
int* arr = new int[10]; // 分配一个包含10个int的数组for(int i = 0; i < 10; ++i) { arr[i] = i; // 初始化数组}delete[] arr; // 释放数组内存arr = nullptr; // 避免野指针
2. 注意事项
内存泄漏:如果分配了内存但没有释放,那么这块内存就会一直占用,直到程序结束。这可能导致程序使用的内存不断增加,最终耗尽系统资源。
野指针:如果释放了内存但没有将指针设置为nullptr
,那么这个指针就变成了野指针。尝试通过野指针访问内存是未定义行为,可能导致程序崩溃。
异常安全:在分配内存后,如果发生异常,那么可能会跳过释放内存的代码。使用智能指针(如std::unique_ptr
和std::shared_ptr
)可以自动管理内存,提高代码的异常安全性。
3. 智能指针
C++11及以后的版本引入了智能指针,它们可以自动管理内存,减少内存泄漏和野指针的风险。
std::unique_ptr
:独占式拥有其所指对象,同一时间内只能有一个std::unique_ptr
指向给定对象(通过禁止拷贝构造函数和拷贝赋值操作符,只提供移动语义)。
std::shared_ptr
:允许多个std::shared_ptr
实例共享同一个对象。当最后一个拥有该对象的std::shared_ptr
被销毁时,对象也会被销毁。
std::weak_ptr
:一种不拥有其所指对象的智能指针,主要用来解决std::shared_ptr
之间循环引用的问题。
4.示例:使用std::unique_ptr
#include <memory>int main() { std::unique_ptr<int> ptr(new int(10)); // 使用ptr... // 当ptr离开作用域时,它指向的内存会自动被释放 return 0;}
使用智能指针可以大大简化动态内存的管理,减少错误,提高代码的安全性和可维护性。
二、内存管理函数
在C++中,内存管理主要通过几种方式实现,包括使用C语言风格的内存管理函数以及C++特有的操作符和智能指针。以下是C++中常见的内存管理函数和机制:
1. C语言风格的内存管理函数
这些函数在C++中仍然可以使用,因为它们被C++继承并兼容。
malloc:用于动态分配指定大小的内存块。分配的内存不会自动初始化,其内容是不确定的。如果分配成功,返回指向分配的内存的指针;如果失败,返回NULL。int* ptr = (int*)malloc(sizeof(int));if (ptr != NULL) { // 使用ptr free(ptr);}
calloc:类似于malloc,但它会额外将分配的内存初始化为零。它接受两个参数:元素的数量和每个元素的大小。 int* arr = (int*)calloc(10, sizeof(int));if (arr != NULL) { // 使用arr free(arr);}
realloc:用于调整之前通过malloc或calloc分配的内存块的大小。如果调整成功,返回指向新内存块的指针(可能与原指针相同,也可能不同);如果失败,返回NULL,并且原内存块保持不变。 int* new_arr = (int*)realloc(arr, 20 * sizeof(int));if (new_arr != NULL) { arr = new_arr; // 使用arr}free(arr);
free:用于释放之前通过malloc、calloc或realloc分配的内存块。同一块内存只能被释放一次,多次释放会导致未定义行为。 free(ptr);
2. C++特有的内存管理操作符
C++引入了new
和delete
操作符来简化内存管理,并自动处理对象的构造和析构。
int* ptr = new int;MyClass* obj = new MyClass;delete ptr;delete obj;
new[]:用于动态分配一个对象数组的内存,并自动调用数组中每个对象的构造函数(如果适用)。 int* arr = new int[10];MyClass* objs = new MyClass[5];delete[] arr;delete[] objs;
delete:用于释放之前通过new分配的内存块,并自动调用对象的析构函数(如果适用)。delete[]:用于释放之前通过new[]分配的内存块,并自动调用数组中每个对象的析构函数(如果适用)。 3. 智能指针
C++11及以后的版本引入了智能指针,如std::unique_ptr
、std::shared_ptr
和std::weak_ptr
,它们可以自动管理内存,减少内存泄漏和野指针的风险。
std::unique_ptr
指向给定对象。std::shared_ptr:允许多个std::shared_ptr
实例共享同一个对象。当最后一个拥有该对象的std::shared_ptr
被销毁时,对象也会被销毁。std::weak_ptr:一种不拥有其所指对象的智能指针,主要用来解决std::shared_ptr
之间循环引用的问题。 智能指针通过封装裸指针并提供自动内存管理功能,使得C++中的动态内存管理更加安全和方便。
三、栈内存和堆内存
在C++中,内存管理是一个核心概念,它涉及到如何分配、使用和释放程序运行时所需的内存。C++程序中的内存主要分为几个区域,其中栈内存(Stack Memory)和堆内存(Heap Memory)是最常见的两种。
1. 栈内存(Stack Memory)
栈内存是自动分配和释放的内存区域。它遵循后进先出(LIFO, Last In First Out)的原则。栈内存主要用于存储局部变量、函数参数和返回值等。每当函数被调用时,它的局部变量和参数就会被推入栈中,并在函数返回时从栈中弹出。栈的大小在程序编译时就已经确定,并且通常比堆小得多。
特点:
自动管理:不需要程序员手动分配和释放内存。速度快:由于栈内存的操作简单且高效,访问速度非常快。有限大小:栈的大小在编译时确定,并且相对较小,可能导致栈溢出(Stack Overflow)错误。2. 堆内存(Heap Memory)
堆内存是动态分配和释放的内存区域。与栈内存不同,堆内存的大小不是固定的,它可以在程序运行时根据需要动态地增长和缩小。堆内存主要用于存储那些大小在程序编译时未知的对象,或者需要在程序的不同部分之间共享的数据。堆内存的分配和释放需要程序员手动管理,通常使用new
和delete
操作符(对于单个对象)或new[]
和delete[]
操作符(对于对象数组)来完成。
特点:
手动管理:程序员需要负责分配和释放内存,这增加了内存泄漏和野指针的风险。灵活:堆内存的大小可以在运行时动态变化,适合存储大小未知或变化的数据。相对较慢:与栈内存相比,堆内存的分配和释放操作更复杂,因此访问速度相对较慢。3. 注意事项
内存泄漏:在堆上分配的内存如果没有被正确释放,就会导致内存泄漏。随着时间的推移,这可能会耗尽系统的可用内存。野指针:如果释放了堆上的内存但没有将指针设置为nullptr
,那么这个指针就变成了野指针。尝试通过野指针访问内存是未定义行为,可能导致程序崩溃。栈溢出:如果栈上的局部变量太多或太大,就可能导致栈溢出错误。这通常发生在递归调用过深或局部变量占用大量内存时。 4. 结论
栈内存和堆内存各有优缺点,适用于不同的场景。栈内存适合存储局部变量和函数参数等小量数据,而堆内存则适合存储大量或动态变化的数据。在使用堆内存时,程序员需要格外注意内存的管理,以避免内存泄漏和野指针等问题。