c++动态内存管理(一)

简介: C++ 动态内存管理在 C++ 中,动态内存管理是一个核心概念,它允许在运行时分配和释放内存。以下是 C++ 动态内存管理需要掌握的关键知识点:

C++ 动态内存管理

在 C++ 中,动态内存管理是一个核心概念,它允许在运行时分配和释放内存。以下是 C++ 动态内存管理需要掌握的关键知识点:

1. new 和 delete 操作符

在 C++ 中,new 和 delete 是用于动态内存分配和释放的基本操作符。

new 操作符

new 用于在堆(也称为自由存储区)上动态分配内存,并返回指向新分配内存的指针。new 会调用对象的构造函数来初始化对象。

基本用法

int* ptr = new int; // 分配一个 int 的内存
*ptr = 5; // 初始化该 int

初始化

new 还支持直接初始化:

int* ptr = new int(5); // 分配内存并初始化为 5

异常

如果内存分配失败(例如由于内存不足),new 将抛出 std::bad_alloc 异常。

数组分配

对于数组的分配,new 提供了特殊的语法:

int* array = new int[10]; // 分配一个包含 10 个整数的数组

delete 操作符

delete 用于释放由 new 分配的内存,并调用对象的析构函数。

基本用法

delete ptr; // 释放内存并调用析构函数

数组释放

释放由 new 分配的数组需要使用 delete[]:

delete[] array; // 释放数组内存

注意事项


避免内存泄漏:每个 new 分配的内存都应该使用对应的 delete 或 delete[] 来释放。

避免悬挂指针:释放内存后,指针变成悬挂指针。应该将指针设为 nullptr。

不要重复释放内存:对同一内存块调用两次 delete 会导致未定义行为。

数组与非数组形式要匹配:用 new[] 分配的内存必须用 delete[] 释放,用 new 分配的内存必须用 delete 释放。

替代方案

在现代 C++ 开发中,建议使用智能指针(如 std::unique_ptr 和 std::shared_ptr)来自动管理动态分配的内存,以避免手动管理内存的复杂性和风险。智能指针在其析构函数中自动释放它们所拥有的内存,从而帮助防止内存泄漏和其他常见的内存管理错误。

2. 动态分配数组

在 C++ 中,动态分配数组涉及使用 new 和 delete 操作符来在堆上分配和释放连续的内存块,用于存储数组。这种机制允许在运行时确定数组的大小,提供了比静态数组更大的灵活性。

使用 new[] 分配数组

new[] 用于动态分配一个元素数量已知的数组。

基本用法

int* myArray = new int[10]; // 分配一个包含 10 个整数的数组

在这个例子中,new[] 分配了一个包含 10 个整数的数组,并返回一个指向数组第一个元素的指针。分配的数组在堆上,其生命周期直到使用 delete[] 显式释放为止。


初始化数组

使用 new[] 分配的数组默认进行值初始化。对于内置类型,如 int,这意味着数组元素被初始化为零。对于类类型的元素,将调用默认构造函数。

使用 delete[] 释放数组

使用 delete[] 来释放由 new[] 分配的数组内存。

基本用法

delete[] myArray; // 释放数组内存

在这个例子中,delete[] 释放了之前分配的数组内存。如果数组元素是类对象,delete[] 还将调用每个对象的析构函数。

注意事项


匹配使用 new[] 和 delete[]:用 new[] 分配的内存必须用 delete[] 释放。使用不匹配的 delete 形式会导致未定义行为。

避免内存泄漏:每个通过 new[] 分配的数组都应该使用 delete[] 释放。

处理数组指针:释放内存后,指针变成悬挂指针。为了安全,应将指针设为 nullptr。

数组大小不可更改:一旦数组被分配,其大小将固定,不能更改。如果需要可调整大小的数组,可以考虑使用 std::vector。

替代方案


在现代 C++ 中,推荐使用 std::vector 或其他标准容器来替代手动管理的动态数组。标准容器提供了更安全、更方便的方式来处理动态分配的数组,自动管理内存,并提供了许多额外的功能,如自动调整大小和元素访问的边界检查。

3. 处理内存分配失败

在 C++ 中,动态内存分配可能会失败,尤其是在内存资源紧张的情况下。处理内存分配失败是动态内存管理的一个重要方面,确保程序在内存不足时能够可靠地运行。


new 的内存分配失败

当使用 new 进行内存分配时,如果系统无法满足分配请求,将抛出 std::bad_alloc 异常。这是 C++ 标准库中定义的一个异常类型,用来表示内存分配失败。

示例

try {
    int* myArray = new int[1000000000]; // 尝试分配大量内存
} catch (const std::bad_alloc& e) {
    std::cerr << "Memory allocation failed: " << e.what() << std::endl;
    // 处理内存分配失败的情况
}

在这个例子中,如果内存分配失败,程序会捕获 std::bad_alloc 异常,并通过 catch 块来处理这种情况。

new (nothrow) 和内存分配失败

C++ 还提供了一种不抛出异常的内存分配方式,即 new (std::nothrow)。如果使用 new (std::nothrow) 进行内存分配,当内存分配失败时,它不会抛出异常,而是返回一个空指针。

示例

#include <new> // 引入 std::nothrow

int* myArray = new(std::nothrow) int[1000000000];
if (myArray == nullptr) {
    std::cerr << "Memory allocation failed." << std::endl;
    // 处理内存分配失败的情况
}

在这个例子中,如果内存分配失败,则 myArray 会被设置为 nullptr,程序可以检查这一点并相应地处理。

注意事项

  • 及时处理分配失败:应检查每次 new 或 new (nothrow) 调用的返回值,并在内存分配失败时进行适当处理。
  • 避免资源泄露:在处理内存分配失败时,应确保已分配资源的正确释放,避免资源泄露。
  • 考虑备选方案:在内存紧张的环境中,考虑使用更小的内存请求,或使用备选的数据结构和算法。


4. 智能指针

在 C++ 中,智能指针是用于自动管理动态分配的内存的对象。智能指针通过在其析构函数中释放关联的内存,帮助避免内存泄漏和指针错误。标准库提供了几种类型的智能指针,最常用的包括 std::unique_ptr、std::shared_ptr 和 std::weak_ptr。


std::unique_ptr

std::unique_ptr 是一个独占式智能指针,它拥有其指向的对象,并在 unique_ptr 对象销毁时自动删除关联的对象。

特点


  • 独占所指向的对象。
  • 不允许复制,但可以移动。
  • 自动释放所拥有的对象。


示例

#include <memory>
std::unique_ptr<int> ptr(new int(10)); // 创建 unique_ptr

std::shared_ptr

std::shared_ptr 是一个共享式智能指针,它允许多个 shared_ptr 实例共享同一个对象。当最后一个拥有对象的 shared_ptr 被销毁时,对象被删除。



特点

  • 共享对象的所有权。
  • 使用引用计数来跟踪拥有对象的 shared_ptr 数量。
  • 允许复制和移动。


示例

std::shared_ptr<int> shared1(new int(20));
std::shared_ptr<int> shared2 = shared1; // 共享所有权

std::weak_ptr

std::weak_ptr 是一种不拥有对象的智能指针,它被设计用来观察 std::shared_ptr,但不影响其引用计数。


特点

  • 不拥有对象,不影响 shared_ptr 的引用计数。
  • 用来解决 shared_ptr 相互引用导致的循环引用问题。
  • 可以从 shared_ptr 或另一个 weak_ptr 创建。

示例

std::shared_ptr<int> shared(new int(30));
std::weak_ptr<int> weak = shared; // 观察 shared_ptr

管理策略

  • unique_ptr 适用于资源的独占管理。
  • shared_ptr 适用于资源的共享管理,需要注意循环引用问题。
  • weak_ptr 用于“安全观察” shared_ptr 所管理的资源,避免循环引用。


总结

智能指针在自动管理资源方面提供了极大的方便,是现代 C++ 中管理动态资源的首选方法。使用智能指针可以减少直接使用 new 和 delete 的需要,降低内存泄漏和其他内存错误的风险。



c++动态内存管理(二)https://developer.aliyun.com/article/1437213?spm=a2c6h.13262185.profile.33.5bba685cuSQkDD

目录
相关文章
|
29天前
|
存储 Java C++
C++ 引用和指针:内存地址、创建方法及应用解析
C++中的引用是现有变量的别名,创建时需用`&`运算符,如`string &meal = food;`。指针存储变量的内存地址,使用`*`创建,如`string* ptr = &food;`。引用必须初始化且不可为空,而指针可初始化为空。引用在函数参数传递和提高效率时有用,指针适用于动态内存分配和复杂数据结构操作。选择使用取决于具体需求。
40 9
|
1月前
|
移动开发 监控 数据可视化
通过C++实现对管理员工上网行为的数据挖掘与可视化
这篇文章介绍了如何使用C++进行员工上网行为的监控数据挖掘与可视化。首先,通过读取系统日志收集上网数据,然后进行数据分析,包括统计访问频率和识别异常行为。接着,利用数据可视化工具展示结果。最后,讨论了如何将监控数据自动提交到网站,以实现实时监控和问题响应。示例代码展示了使用Boost.Asio库创建HTTP客户端上传数据的基本过程。
85 2
|
1月前
|
存储 Linux C语言
【C++初阶】6. C&C++内存管理
【C++初阶】6. C&C++内存管理
35 2
|
1天前
|
存储 编译器 C语言
C++中的内存管理
C++中的内存管理
11 0
|
2天前
|
存储 Linux C语言
【C++从练气到飞升】07---内存管理
【C++从练气到飞升】07---内存管理
|
4天前
|
存储 编译器 C++
【C++】内存管理和模板基础(new、delete、类及函数模板)
【C++】内存管理和模板基础(new、delete、类及函数模板)
19 1
|
16天前
|
存储 缓存 算法
C++从入门到精通:4.6性能优化——深入理解算法与内存优化
C++从入门到精通:4.6性能优化——深入理解算法与内存优化
|
16天前
|
存储 程序员 编译器
C++从入门到精通:3.4深入理解内存管理机制
C++从入门到精通:3.4深入理解内存管理机制
|
17天前
|
存储 人工智能 程序员
【重学C++】【内存】关于C++内存分区,你可能忽视的那些细节
【重学C++】【内存】关于C++内存分区,你可能忽视的那些细节
54 1
|
17天前
|
C语言 C++
【C++基础(九)】C++内存管理--new一个对象出来
【C++基础(九)】C++内存管理--new一个对象出来