2013-08-21 51 views
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; 
} 

輸出:呼叫轉到疼痛的call()

但是,如果我做痛::調用()作爲虛擬的(Medicine :: call()是真實的),然後調用去看Comb的調用()。沒有任何問題!

但是當我做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()是否爲虛擬無關)。這是爲什麼發生?

+2

你能稱之爲真正的「醫學/疼痛/梳理」類嗎? – billz

+1

你可以發佈一些重現問題的最小代碼嗎? – juanchopanza

+0

我無法得到你。請詳細說明。 – Smith

回答

8

您正遇到未定義的行爲,因爲基類的析構函數不是virtual。任何事情都可能發生。

如果通過指向基類的指針刪除派生對象,析構函數必須爲virtual。這是一個規則。

0

根據Scott Mayers的「Effective C++」,如果要在程序中定義派生類,則應該在基類中將析構函數定義爲虛擬的,否則程序在運行時會表現出不同的行爲(可能是內存泄漏,輸出錯誤,崩潰) 。

0

虛析構函數是有用的時候,我們可以通過指針刪除派生類的實例基類:

class Base 
{ 
    // some code 
}; 

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

在這裏,我沒有申報基地的析構函數將被虛擬化。現在,讓我們來看看下面的代碼:

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

由於基地的析構函數不是虛擬的,b是一個基本*指向一個派生類對象,刪除B具有不確定的行爲。在我的實現中,對析構函數的調用將像任何非虛擬代碼一樣被解析,這意味着基類的析構函數將被調用,但不是派生類的析構函數,從而導致資源泄漏。 另外,當你使用繼承時,它總是可以安全地聲明析構函數是虛擬的,不管它是否有用。

相關問題