如果分配该数组引发异常,您是否应该释放该数组?
Should you free an array if allocating that array threw an exception?
我有一个潜在的不稳定 class 是别人写的,我必须创建一个 class 的对象数组。我提到 class 是不稳定的,所以它可能偶尔会在默认构造函数中抛出异常。我无权访问源代码,只能访问编译后的二进制文件。
当我使用 new
分配这些类型对象的动态数组时,这些坏对象之一可能会抛出异常。它抛出自定义异常,而不是 std::bad_alloc
。
无论如何,我需要让程序从异常中恢复并继续运行,尽管设置了一些错误标志等等。我认为我应该 delete
与数组关联的内存以防止内存泄漏。
我的理由是,如果 class 在数组中间某处构造元素时抛出异常,则该元素将无法正确构造,并且所有未来的元素都将停止构造异常,但是前面的元素将被正确构造,因为那是在抛出异常之前发生的。我想知道,在 catch (...) { }
中调用 delete
是个好主意吗?我将如何解决此内存泄漏问题?
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
___ __________________________________
| ---------->| :) | :) | :) | :) | :( | | | | | |
|___| |____|____|____|____|____|_|_|_|_|_|
在下面的代码行中:
array = new Badclass[10];
new Badclass[10]
首先计算。如果抛出异常,则执行不会到达分配。 array
保留其先前的值,即 nullptr
。
在 nullptr
.
上调用 delete 无效
评论区问题:
Is this kind of behavior based on the same principle as stack unwinding?
标准中关于"Exception handling"的部分帮助我们理解在分配时抛出异常时会发生什么。
18 Exception handling [except]
...
18.2 Constructors and destructors [except.ctor]
1.As control passes from the point where an exception is thrown to a handler, destructors are invoked by a process, specified in this subclause, called stack unwinding.
...
3.If the initialization or destruction of an object other than by delegating constructor is terminated by an exception, the destructor is invoked for each of the object’s direct subobjects and, for a complete object, virtual base class subobjects, whose initialization has completed and whose destructor has not yet begun execution, except that in the case of destruction, the variant members of a union-like class are not destroyed. The subobjects are destroyed in the reverse order of the completion of their construction. Such destruction is sequenced before entering a handler of the function-try-block of the constructor or destructor, if any.
回答最后一个问题:
How would I go about solving this memory leak?
没有内存泄漏。仅当 BadClass
本身动态分配内容并且从未在其析构函数中释放它时才会发生泄漏。由于我们没有注意到您的 BadClass
实施,而不是猜测,所以这取决于您。 new BadClass[N];
本身泄漏内存的唯一方法是,如果它完成并且您稍后扔掉对它的唯一引用,您正在手动管理 (array
)。
一个动态分配的数组,将其放入其中元素的一个构造函数中,将 (a) 以相反的顺序为已构造的元素退出析构函数,(b) 释放分配的内存,最后 (c) 执行实际抛出到最近的捕获处理程序(或有 none 时的默认处理程序)。
因为发生了抛出,所以对结果数组指针的赋值永远不会发生,因此不需要 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]; // May throw exceptions!
没有内存泄漏。
作为参考阅读 new expression on cppreference:
If initialization terminates by throwing an exception (e.g. from the
constructor), if new-expression allocated any storage, it calls the
appropriate deallocation function: operator delete for non-array type,
operator delete[] for array type.
所以明明写着delete[]
是自动调用的,不用你调用。
如果在抛出异常之前,new[]
构造了部分对象,那么在释放内存之前,将销毁所有构造的对象。这就像包含数组的对象构造一样,在数组中构造某个对象时会抛出异常。
我有一个潜在的不稳定 class 是别人写的,我必须创建一个 class 的对象数组。我提到 class 是不稳定的,所以它可能偶尔会在默认构造函数中抛出异常。我无权访问源代码,只能访问编译后的二进制文件。
当我使用 new
分配这些类型对象的动态数组时,这些坏对象之一可能会抛出异常。它抛出自定义异常,而不是 std::bad_alloc
。
无论如何,我需要让程序从异常中恢复并继续运行,尽管设置了一些错误标志等等。我认为我应该 delete
与数组关联的内存以防止内存泄漏。
我的理由是,如果 class 在数组中间某处构造元素时抛出异常,则该元素将无法正确构造,并且所有未来的元素都将停止构造异常,但是前面的元素将被正确构造,因为那是在抛出异常之前发生的。我想知道,在 catch (...) { }
中调用 delete
是个好主意吗?我将如何解决此内存泄漏问题?
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
___ __________________________________
| ---------->| :) | :) | :) | :) | :( | | | | | |
|___| |____|____|____|____|____|_|_|_|_|_|
在下面的代码行中:
array = new Badclass[10];
new Badclass[10]
首先计算。如果抛出异常,则执行不会到达分配。 array
保留其先前的值,即 nullptr
。
在 nullptr
.
评论区问题:
Is this kind of behavior based on the same principle as stack unwinding?
标准中关于"Exception handling"的部分帮助我们理解在分配时抛出异常时会发生什么。
18 Exception handling [except]
...
18.2 Constructors and destructors [except.ctor]1.As control passes from the point where an exception is thrown to a handler, destructors are invoked by a process, specified in this subclause, called stack unwinding.
...
3.If the initialization or destruction of an object other than by delegating constructor is terminated by an exception, the destructor is invoked for each of the object’s direct subobjects and, for a complete object, virtual base class subobjects, whose initialization has completed and whose destructor has not yet begun execution, except that in the case of destruction, the variant members of a union-like class are not destroyed. The subobjects are destroyed in the reverse order of the completion of their construction. Such destruction is sequenced before entering a handler of the function-try-block of the constructor or destructor, if any.
回答最后一个问题:
How would I go about solving this memory leak?
没有内存泄漏。仅当 BadClass
本身动态分配内容并且从未在其析构函数中释放它时才会发生泄漏。由于我们没有注意到您的 BadClass
实施,而不是猜测,所以这取决于您。 new BadClass[N];
本身泄漏内存的唯一方法是,如果它完成并且您稍后扔掉对它的唯一引用,您正在手动管理 (array
)。
一个动态分配的数组,将其放入其中元素的一个构造函数中,将 (a) 以相反的顺序为已构造的元素退出析构函数,(b) 释放分配的内存,最后 (c) 执行实际抛出到最近的捕获处理程序(或有 none 时的默认处理程序)。
因为发生了抛出,所以对结果数组指针的赋值永远不会发生,因此不需要 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]; // May throw exceptions!
没有内存泄漏。
作为参考阅读 new expression on cppreference:
If initialization terminates by throwing an exception (e.g. from the constructor), if new-expression allocated any storage, it calls the appropriate deallocation function: operator delete for non-array type, operator delete[] for array type.
所以明明写着delete[]
是自动调用的,不用你调用。
如果在抛出异常之前,new[]
构造了部分对象,那么在释放内存之前,将销毁所有构造的对象。这就像包含数组的对象构造一样,在数组中构造某个对象时会抛出异常。