【人工智能教程】,前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。
点击跳转到网站:【人工智能教程】
文章目录
- 一、动态内存
- 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
操作符来简化内存管理,并自动处理对象的构造和析构。
- new:用于动态分配内存,并自动调用对象的构造函数(如果适用)。对于内置类型,它类似于malloc,但不需要类型转换。对于类类型,它还会调用构造函数。
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::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. 结论
栈内存和堆内存各有优缺点,适用于不同的场景。栈内存适合存储局部变量和函数参数等小量数据,而堆内存则适合存储大量或动态变化的数据。在使用堆内存时,程序员需要格外注意内存的管理,以避免内存泄漏和野指针等问题。
四、相关链接
- Visual Studio Code下载地址
- Sublime Text下载地址
- 「C++系列」C++简介、应用领域
- 「C++系列」C++ 基本语法
- 「C++系列」C++ 数据类型
- 「C++系列」C++ 变量类型
- 「C++系列」C++ 变量作用域
- 「C++系列」C++ 常量知识点-细致讲解
- 「C++系列」C++ 修饰符类型
- 「C++系列」一篇文章说透【存储类】
- 「C++系列」一篇文章讲透【运算符】
- 「C++系列」循环
- 「C++系列」判断
- 「C++系列」函数/内置函数
- 「C++系列」数字/随机数
- 「C++系列」数组
- 「C++系列」字符串
- 「C++系列」指针
- 「C++系列」引用
- 「C++系列」日期/时间
- 「C++系列」输入/输出
- 「C++系列」数据结构
- 「C++系列」vector 容器
- 「C++系列」类/对象
- 「C++系列」继承
- 「C++系列」重载运算符/重载函数
- 「C++系列」多态
- 「C++系列」数据抽象
- 「C++系列」数据封装
- 「C++系列」 接口(抽象类)
- 「C++系列」文件和流
- 「C++系列」异常处理