我最近的任务是找出我们代码的一部分中的内存泄漏。泄漏最终在一个特定object...and的析构函数中,我发现了一些非常奇怪的东西。一位前同事写道:
File::~File()
try
{
Clear();
}
catch (...)
{
Log("caught exception");
}file类继承自一些基类。我的第一个问题是:这是严格合法的C++吗?它在Visual Studio2008中编译,但我向一些朋友/同事展示了它,他们对它的工作感到相当震惊。
然而,它实际上并不能像预期的那样工作:这个对象继承的基类有一个析构函数,这个析构函数现在永远不会被调用(而不是将析构函数包装在一个常规的方法块中,将try / catch作为该方法的一部分)。
任何人都可以尝试解释为什么允许这样做,为什么基类析构函数没有被调用?这里的析构函数并没有抛出。
发布于 2011-05-10 23:53:40
这是一个函数try块,它是完全合法的。
例如,请参见here。
在函数try块中可以做的事情,而不能在函数中的普通try块中做的唯一一次是捕捉构造函数初始化器列表中的表达式抛出的异常(即使这样,您最终也必须抛出一些东西),但这不适用于这里。
这个GOTW #66特别有趣,尽管它更专注于构造函数。它包含了这样的“道德”:
因为析构函数永远不会发出异常,所以析构函数的
-try-block根本没有实际用途。
为了补充说明,所编写的代码将导致由于ISO/IEC 14882:200315.3 except.handle /16而捕获的任何异常被重新抛出:
如果控制到达构造函数或析构函数的function-try-block的处理程序的末尾,则重新抛出正在处理的异常。..。
然而,在析构函数的函数try块的处理程序中有一个无参数的return是合法的-它只在构造函数的函数try块中被禁止-这将抑制异常的重新抛出。因此,这两种方法中的任何一种都可以防止异常离开析构函数。
File::~File()
try
{
Clear();
}
catch (...)
{
Log("caught exception");
return;
}File::~File()
{
try
{
Clear();
}
catch (...)
{
Log("caught exception");
}
}发布于 2011-05-11 03:10:09
为了回答第二部分,“为什么基类析构函数没有被调用?”,12.4/6:
在执行析构函数的主体并销毁主体中分配的任何自动对象之后,类X的析构函数调用X的直接成员的析构函数,X的直接基类的析构函数...析构函数中的return语句(6.6.3)可能不会直接返回给调用方;在将控制权移交给调用方之前,成员和基类的析构函数会被调用。
这并不是说如果析构函数抛出,就会调用成员和基析构函数。然而,15.2/2说:
部分构造或部分销毁的对象将具有针对其全部构造的子对象执行的析构函数(
),
我认为,无论是因为析构函数主体抛出的异常,还是因为析构函数的try函数块抛出的异常,对象被“部分销毁”都应该是正确的。我非常确定“在析构函数的主体之后”应该也意味着在函数try块之后。
显然微软并不同意,而且由于函数try块,它没有生成“析构函数的主体”,也没有执行“析构函数的主体”之后发生的事情。
我觉得这听起来不太对劲。无论派生类dtor函数try块抛出与否,GCC 4.3.4都会执行基类析构函数。在它抛出的情况下,基会在catch子句执行之前被析构。
https://stackoverflow.com/questions/5952837
复制相似问题