当我使用Visual Studio 2010运行以下C ++代码时,如果任何派生类函数声明为虚拟,则程序会停止删除变量。有人可以解释一下吗?
void testInheritance()
{
class a
{
public :
char x;
void fn1()
{
std::cout<<"\n In Class A Function 1 : "<<x;
}
virtual void fn2()
{
std::cout<<"\n In Class A Function 2 : "<<x;
}
a()
{
x='A';
std::cout<<"\n In A() : "<<x;
}
~a()
{
std::cout<<"\n In ~A : "<<x;
}
};
class b: public a
{
public :
char y;
virtual void fn1()
{
std::cout<<"\n In Class B Function 1 : "<<y;
}
void fn3()
{
std::cout<<"\n In Class B Function 3 : "<<y;
}
b()
{
y='B';
std::cout<<"\n In B() : "<<y;
}
~b()
{
std::cout<<"\n In ~B : "<<y;
}
};
a* var = new b();
delete var;
}
更多信息:
我理解为了调用b :: fn1和类b的析构函数,我需要在基类中声明它们是虚拟的,即类a。但是,如果我不这样做,甚至不将类b中的任何函数(也不是来自类a)声明为虚拟,它应该调用a的fn1和析构函数,并且这完全发生。但是,当我声明b(但不是a)的任何成员是虚拟的,无论是新成员还是重载成员,然后它在使用VS2010编译时挂起并在使用gcc4.4.4在linux上编译时中断。它应该调用任一个析构函数并正常工作,但我无法理解为什么程序会中断的原因。
此外,在Visual Studio 2010中使用Intellitrace时,我尝试在挂起代码时破坏代码,我收到以下消息:
该过程似乎已死锁(或未运行任何用户模式代码)。所有线程都已停止。
答案 0 :(得分:4)
您期待意外行为,因为您在程序中创建了未定义行为。
使用指向derived
类的base
类删除non-virtual destructor
类对象,结果为Undefined Behavior
。未定义的Beahavor意味着任何事情都可能发生。
C ++标准第1.3.24节规定:
允许的未定义行为包括完全忽略不可预测的结果,在翻译或程序执行期间以环境特征(有或没有发出诊断消息)的文档方式执行,终止翻译或执行(发出诊断信息)。
如何解决问题?
Base类中的析构函数应该是虚拟的。
答案 1 :(得分:2)
您的析构函数不是虚拟的,您是否不允许delete
var
作为基类指针。根据其他虚拟函数的存在,您很可能只有两组行为。
答案 2 :(得分:2)
你需要声明析构函数virtual
答案 3 :(得分:1)
如果“卡住”表示未调用b::~b()
,则答案是a::~a()
需要virtual
。
您正在使用基类(a
)指针来保存class b
的对象。当您delete var;
时,它仅调用a::~a()
,而不是virtual
;通过制作virtual
;按正确顺序调用析构函数a
和b
。
[注意:另一种方式它只能被击中,如果你在某个地方放置了一个断点并且你没有踩过去。 :)]
答案 4 :(得分:0)
我实际上厌倦了在C ++测试中看到,询问在这种情况下的行为是什么。他们希望你回答它会调用A的析构函数而不是B的析构函数。
这不是保证的行为,你不能依赖它。未定义的行为意味着您无法确定将会发生什么,这就是这种情况。
它也是“只是不要这样做......”的一个例子。在我上一份工作中,我完全从测试此行为的系统中删除了一个测试,理由是它不相关且不合适。
使a
虚拟析构函数的另一种选择是保护它。这也将保护您main()
无法编译,因为您无法从那里调用delete var
。您甚至无法通过b
执行与main
相同的操作来调用delete
中未定义的行为,因为您可能会感到惊讶,但a*
boost::shared_ptr<a>( new b );
也无法访问a
。
{{1}}
安全,因为它会为b而不是a创建一个删除器。
由于{{1}}中还有另一个虚函数,但你几乎肯定会选择使其析构函数为虚拟函数。