虚拟函数的意外行为

Unexpected behaviour of virtual function?

本文关键字:意外 函数 虚拟      更新时间:2023-10-16

当我用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编译时,它都会挂起,当在linux上使用gcc4.4.4编译时,会中断。它本应该调用任意一个析构函数并正常工作,但我不明白程序崩溃的原因。

此外,当在VisualStudio2010中使用Intellitrace时,我试图在代码挂起的地方中断代码,我得到以下消息:

进程似乎处于死锁状态(或者没有运行任何用户模式代码(。所有线程都已停止。

由于您在程序中创建了未定义的行为,因此您预期会出现意外行为

使用指向具有non-virtual destructorbase类的指针删除derived类对象会导致Undefined Behavior。未定义的Beahavor意味着任何事情都可能发生。

C++标准第1.3.24节规定:

允许的未定义行为范围从完全忽略具有不可预测结果的情况,到在翻译或程序执行过程中以环境特征的记录方式行事(有或没有发布诊断消息(,再到终止翻译或执行(有诊断消息发布(

如何解决问题

基类中的析构函数应为虚拟的。

您的析构函数不是虚拟的,因为您不允许将delete var作为基类指针。最有可能的是,根据其他虚拟函数的存在,您只得到了两组行为。

您需要声明析构函数虚拟

如果"stick"表示不调用b::~b(),那么答案是a::~a()需要是virtual

您正在使用基类(a(指针来保存class b的对象。当您delete var;时,它只调用不是virtuala::~a();使其成为CCD_ 15;对CCD_ 16和CCD_。

[注意:另一种方式是,只有当你在某个地方放置了断点而没有通过时,它才能被击中。:(]

实际上,我已经受够了在C++测试中看到的东西,问在这种情况下会有什么行为。他们想让你回答,它会调用A的析构函数,但不会调用B的析构因子。

这不是有保证的行为,你不能依赖它。未定义的行为意味着你不能确定会发生什么,这里就是这样。

这也是一个"只是不要做…"的例子。在我的上一份工作中,我完全从测试这种行为的系统中删除了一个测试,理由是它无关紧要,偏离了主题。

使a的析构函数成为虚拟的另一种选择是使其受到保护。这也将保护您,因为main()将无法编译,因为您无法从那里调用delete var。您甚至不能像main那样调用b中的未定义行为,因为您可能会感到惊讶,但deletea*在那里也无法访问。

boost::shared_ptr<a>( new b );

安全地,因为它将为b而不是a创建一个deleter。

由于a中有另一个虚拟函数,因此您几乎可以肯定地选择将其析构函数设为虚拟函数。