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

目录
相关文章
|
2月前
|
编译器 C++ 开发者
【C++】深入解析C/C++内存管理:new与delete的使用及原理(三)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
|
1月前
|
存储 缓存 C语言
【c++】动态内存管理
本文介绍了C++中动态内存管理的新方式——`new`和`delete`操作符,详细探讨了它们的使用方法及与C语言中`malloc`/`free`的区别。文章首先回顾了C语言中的动态内存管理,接着通过代码实例展示了`new`和`delete`的基本用法,包括对内置类型和自定义类型的动态内存分配与释放。此外,文章还深入解析了`operator new`和`operator delete`的底层实现,以及定位new表达式的应用,最后总结了`malloc`/`free`与`new`/`delete`的主要差异。
52 3
|
1月前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
88 4
|
2月前
|
存储 程序员 编译器
简述 C、C++程序编译的内存分配情况
在C和C++程序编译过程中,内存被划分为几个区域进行分配:代码区存储常量和执行指令;全局/静态变量区存放全局变量及静态变量;栈区管理函数参数、局部变量等;堆区则用于动态分配内存,由程序员控制释放,共同支撑着程序运行时的数据存储与处理需求。
154 21
|
2月前
|
程序员 C++ 容器
在 C++中,realloc 函数返回 NULL 时,需要手动释放原来的内存吗?
在 C++ 中,当 realloc 函数返回 NULL 时,表示内存重新分配失败,但原内存块仍然有效,因此需要手动释放原来的内存,以避免内存泄漏。
|
2月前
|
存储 C语言 C++
【C++打怪之路Lv6】-- 内存管理
【C++打怪之路Lv6】-- 内存管理
48 0
【C++打怪之路Lv6】-- 内存管理
|
2月前
|
存储 C语言 C++
【C/C++内存管理】——我与C++的不解之缘(六)
【C/C++内存管理】——我与C++的不解之缘(六)
|
2月前
|
程序员 C语言 C++
C++入门5——C/C++动态内存管理(new与delete)
C++入门5——C/C++动态内存管理(new与delete)
83 1
|
2月前
|
编译器 C语言 C++
详解C/C++动态内存函数(malloc、free、calloc、realloc)
详解C/C++动态内存函数(malloc、free、calloc、realloc)
353 1
|
2月前
|
存储 安全 程序员
【C++篇】深入内存迷宫:C/C++ 高效内存管理全揭秘
【C++篇】深入内存迷宫:C/C++ 高效内存管理全揭秘
92 3