如果分配该数组引发异常,你应该释放一个数组吗?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如果分配该数组引发异常,你应该释放一个数组吗?相关的知识,希望对你有一定的参考价值。
我有一个别人写的潜在不稳定的类,我不得不创建该类的对象数组。我提到该类是不稳定的,因此它可能偶尔会在默认构造函数中抛出异常。我无权访问源代码,只能访问已编译的二进制文件。
当我使用new
分配这些类型的对象的动态数组时,这些坏对象之一可能会抛出异常。它抛出一个自定义异常,而不是std::bad_alloc
。无论如何,我需要让程序从异常中恢复并继续使用,尽管设置了一些错误标志,什么不是。我认为我应该delete
与数组相关的内存,以防止内存泄漏。
我的理由是,如果类抛出在数组中间某处构造元素的异常,那么该元素将无法正确构造,并且所有未来元素将被异常停止构造,但之前的元素将被因为在抛出异常之前发生了这种情况。我想知道,在delete
打电话给catch (...) { }
是个好主意吗?我该如何解决这个内存泄漏?
Badclass* array = nullptr;
try {
array = new Badclass[10]; // May throw exceptions!
} catch (...) {
delete[] array;
array = nullptr;
// set error flags
}
这是我在内存中可视化的方式。它是否正确?
array 0 1 2 3 4 5 6 7 8 9
___ __________________________________
| ---------->| :) | :) | :) | :) | :( | | | | | |
|___| |____|____|____|____|____|_|_|_|_|_|
回答最后一个问题:
我该如何解决这个内存泄漏?
没有内存泄漏。只有当BadClass
本身动态分配内容并且从未在其析构函数中释放它时,才会发生泄漏。既然我们忘记了你的BadClass
实现,而不是猜测,那取决于你。 new BadClass[N];
本身泄漏内存的唯一方法是,如果它完成,你后来扔掉了你手动管理的唯一引用(array
)。
一个动态分配的数组,在其中一个元素的构造函数中抛出,将(a)为已经构造的元素以相反的顺序退出析构函数,(b)释放分配的内存,最后(c)主持实际投掷到最近的catch处理程序(或没有时的默认处理程序)。
因为抛出发生,对结果数组指针的赋值永远不会发生,因此不需要delete[]
。
最佳示例:
#include <iostream>
struct A
{
static int count;
int n;
A() : n(++count)
{
std::cout << "constructing " << n << '\n';
if (count >= 5)
throw std::runtime_error("oops");
}
~A()
{
std::cout << "destroying " << n << '\n';
}
};
int A::count;
int main()
{
A *ar = nullptr;
try
{
ar = new A[10];
}
catch(std::exception const& ex)
{
std::cerr << ex.what() << '\n';
}
}
产量
constructing 1
constructing 2
constructing 3
constructing 4
constructing 5
destroying 4
destroying 3
destroying 2
destroying 1
oops
请注意,因为元素'5'的构造从未完成,所以它的析构函数不会被触发。但是,成功构建的成员将被破坏(在上面的示例中没有演示,但如果你正在努力,这是一个有趣的练习)。
所有这些都说,无论如何使用智能指针。
在以下代码行中:
array = new Badclass[10];
首先评估new Badclass[10]
。如果抛出异常,则执行不会到达赋值。 array
保留了之前的价值,即nullptr
。它无法在nullptr
上调用delete。
评论部分提出的问题:
这种行为是否基于与堆栈展开相同的原理?
标准中的“异常处理”部分有助于我们了解在分配时抛出异常时会发生什么。
18异常处理[除外] ... 18.2构造函数和析构函数[except.ctor]
1.当控制从抛出异常的点传递到处理程序时,析构函数由本子条款中指定的进程调用,称为堆栈展开。 ... 3.如果除了通过委托构造函数之外的对象的初始化或销毁由异常终止,则为每个对象的直接子对象调用析构函数,对于完整对象,调用其初始化已完成的虚拟基类子对象,并且析构函数尚未开始执行,除了在破坏的情况下,类型联合类的变体成员不会被销毁。子对象以完成构造的相反顺序销毁。在进入构造函数或析构函数的函数try-block的处理程序(如果有的话)之前,对这种破坏进行排序。
如果出现异常,则无需调用删除:
array = new Badclass[10]; // May throw exceptions!
没有内存泄漏。
作为参考阅读有关new expression on cppreference:
如果初始化通过抛出异常(例如从构造函数)终止,如果new-expression分配了任何存储,则调用适当的释放函数:operator delete for non-array type,operator delete [] for array type。
所以它清楚地说明delete[]
是自动调用的,你不必调用它。
如果在抛出异常之前由new[]
构造了部分对象,那么在释放内存之前,所有已构造的对象都将被破坏。这与包含数组的对象构造类似,并且在构造数组中的某个对象时抛出异常。
以上是关于如果分配该数组引发异常,你应该释放一个数组吗?的主要内容,如果未能解决你的问题,请参考以下文章
C++ 关于内存泄露问题。内存泄露是指用new 分配的内存没有用delete释放,如果未释放会有啥后果?
在函数中定义数组:float fltNum[10],该数组在内存中是如何被分配内存空间?何时被释放内存空间?c语言中