0

在我的代码中,我使用了三个类。请参阅下面的实现:

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 的 call()

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

但是当我做Medicine *p[2]而不是时Pain *p[2],会发生以下错误

*** 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)

为什么这样?当我对 Medicine::call() 使用 virtual 时,这再次消失。(这个问题与 Pain::call() 是否为虚拟无关)。为什么会这样?

4

3 回答 3

8

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

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

于 2013-08-21T11:57:12.503 回答
0

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

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 是指向 Derived 对象的 Base*,因此 delete b 具有未定义的行为。在我的实现中,对析构函数的调用也将像任何非虚拟代码一样被解析,这意味着基类的析构函数将被调用,而不是派生类的析构函数,从而导致资源泄漏。此外,在使用继承时将析构函数声明为虚拟总是安全的,无论它看起来是否有用。

于 2013-08-22T05:34:44.700 回答
0

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

于 2013-08-21T12:31:23.073 回答