C++中抽象类派生类中的不破坏阶段
我的一个同事问,为什么他们的测试会失败。异常描述是纯虚拟调用。快速查看下面一个过于简化的示例代码表明,他们的测试引用了在其作用域过期后在堆栈上创建的对象,因为它们保留了过时的裸指针。简单 然后我的同事问,为什么他们的测试运行得更早,因为她唯一正确添加的东西,我可以说是抽象类中的虚拟析构函数!坦白说,我不知道,我希望有人能启发我 我已经调试了应用程序,在从IFooListener中删除Destructor声明后,应用程序运行良好,vftable指针指向具体类的vftable。因此程序运行,printfs打印。一旦虚拟析构函数进入,在对象退出其作用域后,vftable指针将指向抽象类的vftable 我还尝试了一个非虚拟的析构函数,结果是一样的——应用程序崩溃并烧毁了,就像它应该做的那样 我该如何理解这一点? 如果析构函数(虚或非虚)未在抽象类中声明和定义,则不会生成并调用基类析构函数来清理vftable 使用VS2008编译器在Windows下进行测试和调试 下面是对麻烦代码的简化重构:C++中抽象类派生类中的不破坏阶段,c++,inheritance,abstract-class,virtual-functions,C++,Inheritance,Abstract Class,Virtual Functions,我的一个同事问,为什么他们的测试会失败。异常描述是纯虚拟调用。快速查看下面一个过于简化的示例代码表明,他们的测试引用了在其作用域过期后在堆栈上创建的对象,因为它们保留了过时的裸指针。简单 然后我的同事问,为什么他们的测试运行得更早,因为她唯一正确添加的东西,我可以说是抽象类中的虚拟析构函数!坦白说,我不知道,我希望有人能启发我 我已经调试了应用程序,在从IFooListener中删除Destructor声明后,应用程序运行良好,vftable指针指向具体类的vftable。因此程序运行,prin
#include <stdio.h>
#include <vector>
class IFooListener
{
public:
virtual void onFoo() = 0;
virtual ~IFooListener();
};
IFooListener::~IFooListener() {
// nothing to do!
}
class TestListener : public IFooListener
{
public:
TestListener()
{
m_FooCounter = 0;
}
virtual void onFoo()
{
++m_FooCounter;
printf("Got foo!\n");
}
unsigned int getFooCount()
{
return m_FooCounter;
}
private:
unsigned int m_FooCounter;
};
class FooSource
{
public:
void sendFoo();
void addListner(IFooListener * pListener);
private:
std::vector<IFooListener *> m_Listeners;
};
void FooSource::sendFoo()
{
for (std::vector<IFooListener *>::const_iterator i = m_Listeners.begin(); i != m_Listeners.end(); i++)
{
IFooListener * listener = *i;
listener->onFoo();
}
}
void FooSource::addListner(IFooListener * a_pListener)
{
m_Listeners.push_back(a_pListener);
}
void runTest(FooSource& source, int id)
{
switch (id) {
case 1:
{
TestListener listener1;
source.addListner(&listener1);
source.sendFoo();
if (0 == listener1.getFooCount())
{
printf("Test 1 failed!\n");
}
break;
}
case 2:
{
TestListener listener1;
TestListener listener2;
source.addListner(&listener1);
source.addListner(&listener2);
source.sendFoo();
if ((0 == listener1.getFooCount()) || (0 == listener2.getFooCount()))
{
printf("Test 2 failed!\n");
}
break;
}
default:
printf("Unknown test\n");
break;
}
}
int main()
{
FooSource source;
runTest(source, 1);
runTest(source, 2);
printf("Testing finished\n");
return 0;
}
仅解决析构函数问题: 这里没有发生多态性破坏,因此父级中缺少虚拟析构函数并不妨碍子级被清理。具体来说,本地TestListener在超出范围时会调用自己的析构函数,因为编译器已经知道要销毁的对象的确切类型!因为它知道确切的类型,所以不需要使用虚拟析构函数来清理子实例
它在一个案例中崩溃而没有崩溃的原因请注意:我并不是说它之所以有效是因为它不起作用——结果似乎只是起作用,因为这两个选项都是未定义行为的合法示例。实际上,这是因为处理抽象析构函数调用的代码与非抽象调用的代码不同。这是一种未定义的行为,因此任何事情都可能影响结果。不值得花太多时间去推理。我不清楚你到底尝试了什么,所以我只能猜测是什么让你困惑。无论如何,第一件事是在破坏过程中,对象改变了它的类型,类似于构造。这意味着在某种程度上,动态类型是抽象基类的类型,包括对有效虚函数的必要调整。第二件事是编译器可以省略遵循“仿佛”规则的任何内容。特别是,当做某件事导致未定义的行为时,它可以假设这不会发生,就像在对象被销毁后访问对象一样。@juanchopanza试图在对象超出范围后使用它显然会产生未定义的行为,这一点很清楚。但是它是从抽象类派生出来的,没有明确的析构函数声明,足以进入未定义的领域吗?好吧,在修复悬吊指针问题之后,你是否尝试过没有虚拟析构函数?如果我处理悬空指针问题,就没有什么可考虑的了。你可能是对的,这不值得深入研究,我只是好奇为什么它工作,而它显然不应该。我确切地知道为什么程序崩溃。我想知道,当显式声明的析构函数从抽象基类定义中移除时,它为什么会工作。