堆栈展开如何与析构函数调用有关?
How does stack unwinding work regarding destructor calls?
让我们假设以下简单的例子:
#include <iostream>
struct foo {
~foo() {
std::cout << "~foo()" << std::endl;
}
};
struct bar {
foo x;
bar() : x() {
throw -1;
}
~bar() {
std::cout << "~bar()" << std::endl;
}
};
struct baz {
~baz() {
std::cout << "~baz()" << std::endl;
}
};
int main() {
try {
baz y;
bar z;
} // Destructor is called for everything fully constructed up to here?
catch(...) {
}
}
输出为
~foo()
~baz()
所以很明显bar
的析构函数没有被调用。
对于打算在bar
析构函数中释放的任何资源分配,这意味着什么?
例如
struct bar {
CostlyResource cr;
bar() {
cr.Open(); // Aquire resource
// something else throws ...
throw -1;
}
~bar() {
if(cr.IsOpen()) {
cr.Release(); // Free resource
}
}
};
为了异常安全实现,我可以做些什么来确保正确释放bar
的资源成员?
为了异常安全实现,我该怎么做才能确保 bar 的资源成员被正确释放?
您可以在构造函数中catch
、处理和重新抛出匿名 excp:
struct bar {
CostlyResource cr;
bar() {
try { // Wrap the whole constructor body with try/catch
cr.Open(); // Aquire resource
// something else throws ...
throw -1;
}
catch(...) { // Catch anonymously
releaseResources(); // Release the resources
throw; // Rethrow the caught exception
}
}
~bar() {
releaseResources(); // Reuse the code ro release resources
}
private:
void releaseResources() {
if(cr.IsOpen()) {
cr.Release(); // Free resource
}
}
};
请在此处查看完整的示例代码。
由于这是在构造函数中完成的动态内存分配经常要求的问题,例如
class MyClass {
TypeA* typeAArray;
TypeB* typeBArray;
public:
MyClass() {
typeAAArray = new TypeA[50];
// Something else might throw here
typeBAArray = new TypeB[100];
}
~MyClass() {
delete[] typeAAArray;
delete[] typeBAArray;
}
};
最简单的方法是使用适当的容器(例如std::vector<TypeA>
,std::vector<TypeB>
( 或智能指针(例如std::unique_ptr<TypeA[50]>
(。
对象的生存期在构造函数完成之前不会开始。 如果从构造函数引发异常,则不会调用该对象的析构函数。当然,任何已经构造的子对象都将以与构造相反的顺序被破坏,就像您在第一个示例中看到的那样,~foo()
出现。
第二个示例中的代码不是异常安全的。CostlyResource
设计不佳 - 它自己的析构函数应该释放资源。那么你的代码就是正确的。
如果你必须使用一个不能正确清理自己的现有类,那么你应该做一个包装器,例如:
struct SafeCostlyResource : CostlyResource
{
~SafeCostlyResource()
{
if (IsOpen())
Release();
}
};
并将其用作cr
的类型。 (注意 - 这是说明性的伪代码,有几种方法可以解决这个问题(。
相关文章:
- 什么时候调用析构函数
- C++-明确何时以及如何调用析构函数
- 在c++中使用向量时,如何调用构造函数和析构函数
- C++ 防止在映射中放置()时调用析构函数
- 调用析构函数以释放动态分配的内存
- C++:使用方法调用析构函数的顺序是什么?
- 向量推回调用析构函数时调用析构函数
- 如何在调用析构函数时优雅地停止/销毁带有阻塞调用C++线程?
- C++,我应该调用析构函数吗?
- 如何获取有关在 Clang LibTooling 中调用析构函数的信息?
- 当我从 std::vector 中的新放置调用析构函数时会发生什么?
- 未调用的初始化静态thread_local结构的构造函数和析构函数
- 为什么这里不调用析构函数
- 在调用 std::bind 的产品后意外调用析构函数
- 为什么在传递给函数而不是构造函数时调用析构函数?
- 如何在C++中调用析构函数
- 为什么为未删除的对象调用析构函数?
- 调用析构函数时出错
- C++ 在不释放内存的情况下调用析构函数
- 从函数调用析构函数返回的对象