在继承中使用delete的意外行为,基指针对象指向派生程度最高的类

Unexpected behaviour using delete in inheritence, with base pointer objects pointing to the most derived class

本文关键字:对象 指针 派生 程度最 继承 delete 意外      更新时间:2023-10-16

在我的代码中,我使用了三个类。参见以下实现:

class Medicine 
{ 
   int a;
}
class Pain:public Medicine 
{
   int b;
}
class Comb:public Pain   
{
    string salt,com;
}

所有类都只有参数化的构造函数。call()就像

call()
{
     cout<<"You are in class the_name_of_the_class"<<endl;
}

我在所有这些函数中定义了一个同名的函数call()。(到目前为止,它们还没有被宣布为虚拟的)

代码如下:

int main()
{       
    Pain *p[2];
    p[0]= new Comb("Salt","Com",2,110);
    p[1]= new Comb("SALT","COM",1,100);
    p[0]->call();
    delete p[0];
    delete p[1];
    return 0;
}

输出:呼叫转到Pain的呼叫()

然而,如果我将Pain::call()设为虚拟的(Medicine::call(是真实的),那么调用将转到Comb的call()。没问题!

但是当我做CCD_ 3而不是CCD_,出现以下错误

*** glibc detected *** ./a.out: free(): invalid pointer: 0x00000000022ed078 ***
======= Backtrace: =========
/lib64/libc.so.6[0x3b64a760e6]
./a.out[0x400efe]
/lib64/libc.so.6(__libc_start_main+0xfd)[0x3b64a1ecdd]
./a.out[0x400b79]
======= Memory map: ========

更多的事情发生在这里,这以结束

Abort(core dumped)

为什么会这样?当我将virtual用于Medicine::call()时,这种情况再次消失。(这个问题与Pain::call()是否为虚拟无关)。为什么会发生这种情况?

由于基类的析构函数不是virtual,因此您遇到了未定义的行为。任何事情都有可能发生。

如果通过指向基类的指针删除派生对象,则析构函数必须是virtual。这是一条规则。

根据Scott Mayers的"Effective C++",如果要在程序中定义派生类,则应在基类中将析构函数定义为虚拟的,否则程序在运行时会有不同的行为(可能是内存泄漏、错误输出、崩溃)。

当我们可以通过指向基类的指针删除派生类的实例时,虚拟析构函数很有用:

class Base 
{
   // some code
};
class Derived : public Base
{
    ~Derived()
    {
        // some code
    }
}

在这里,我没有声明Base的析构函数是虚拟的。现在,让我们来看看以下代码:

Base *b = new Derived();
// use b
delete b; // Here's the problem!

由于Base的析构函数不是虚拟的,并且b是指向派生对象的Base*,因此删除b具有未定义的行为。在我的实现中,对析构函数的调用也将像任何非虚拟代码一样被解析,这意味着基类的析构函数将被调用,而不是派生类的析构因子,从而导致资源泄漏。此外,在使用继承时,将析构函数声明为虚拟的总是安全的,无论它是否有用。