我刚接到面试问题,面试官问了
如何告诉析构函数何时应该被调用?
如果没有调用析构函数,你会怎么做?
说实话,我不知道答案。我的猜测是将析构函数放在try catch
块中,但我从未见过人们这样做。有更好的解决方案吗?
答案 0 :(得分:7)
有很多方法可以无法调用对象的析构函数:
abort
或_exit
(偶数exit
会使堆栈变量不受影响)。delete
的数组上调用new []
是调用未定义行为的一种方法,一种常见的行为是仅调用第一个对象的析构函数(留下第二个及后续不受欢迎的行为) - 但它是&#39 ; s仍然是未定义的行为。delete
的指针上调用new
(如果您有内存泄漏,这尤其有问题)。 (这实际上是一个特别常见的情况"析构函数不应该已经运行")。如果您正在尝试调试程序并想知道是否正在调用析构函数,那么
答案 1 :(得分:3)
这是另一个经典的无破坏:
#include <iostream>
#include <memory>
class Base
{
public:
Base()
{
std::cout << "All your base, sucker!" << std::endl;
}
~Base() <-- note lack of virtual
{
std::cout << "Base destroyed!" << std::endl;
}
};
class Sub: public Base
{
public:
Sub()
{
std::cout << "We all live in a Yellow Submarine..." << std::endl;
}
~Sub()
{
std::cout << "Sub destroyed" << std::endl;
}
};
int main()
{
std::unique_ptr<Base> b(new Sub());
}
输出:
All your base, sucker!
We all live in a Yellow Submarine...
Base destroyed!
由于Base
的析构函数不是虚拟的,因此在销毁时会调用~Base
而不是~Sub
,并且~Base
不知道Sub
甚至存在并且无法致电~Sub
完成清理工作。
答案 2 :(得分:2)
例如,您可以在要测试的类中放置一个静态bool,在构造函数中将其设置为true,在析构函数中将其设置为false。当没有调用析构函数时,bool将保持为真。或者它可以是静态int,构造函数中的增量和析构函数中的减量(以及检查范围之前和之后的计数)。这是检查资源泄漏的简单方法之一。我已经在单元测试中使用这种技术来轻松检查当自定义智能指针超出范围时是否调用了正确的构造函数。
在许多情况下可能不会调用析构函数,通常是编程错误导致的。例如:
答案 3 :(得分:0)
我不知道面试官想问你什么,因为背景不明确,但以下几点可能会有所帮助
对于堆栈上的对象 - 在对象超出范围时调用析构函数。
对于在堆上创建的对象 - 对于new创建的每个对象,delete将调用析构函数。如果程序在删除之前终止,则可能不会调用析构函数,在这种情况下应该进行正确的处理(我建议使用智能指针来避免这种情况)
答案 4 :(得分:0)
以下是未调用析构函数的示例:
#include <iostream>
class A {
public:
~A() { std::cout << "Destructor called" << std::endl;}
};
int main()
{
A *a = new A;
return 0;
}
还有很多其他例子。像铸造,静电,...
答案 5 :(得分:0)
检测“负面事件”并不容易:没有发生的事情。
相反,我们测试的是无条件发生的事件,并且总是在我们试图检测的有趣事件之后(当事件确实发生时)。当其他情况发生时,我们就知道我们已经超过了有趣的事情应该发生的时间点(如果它发生的话)。在那时,我们有理由寻找一些确定有趣事件是否发生的积极证据。
例如,我们可以让析构函数设置某种标志,或调用一些回调函数或其他。我们也知道C ++程序按顺序执行语句。因此,假设我们不知道在S1
中执行语句S1 ; S2
期间是否调用了给定的析构函数。我们只是在执行S1
之前安排收集证据,然后在S2
之内或之后,我们寻找那些证据(是标志集,是调用的回调,......)< / p>
如果这只是在调试期间,那么请使用调试器或代码覆盖工具!
如果你想知道“当我运行这样的代码时执行了这行代码”,那么会在其上放置一个调试器断点。。
或运行代码覆盖率工具然后分析结果:它会告诉您程序行的次数。未执行的行将被标记为从未到达(无覆盖)。代码覆盖可以累积来自程序的多次运行的覆盖信息;它们可以帮助您找到未被测试用例命中的代码。