2012-06-14 53 views
1

下面的代碼創建一個雙指針B**B*。它會使用該指針的另一個指針將指向創建B實例分配內存時start()叫做:調用從析構函數中刪除會導致堆棧溢出?

class A

class A 
{ 
public: 
    A() 
    { 
     fb = new B*; 
     *fb = NULL; 
    } 

    ~A() 
    { 
     if(*fb) 
      delete *fb; 

     delete fb; 
    } 

    B** getfb() 
    { 
     return fb; 
    } 

private: 
    B** fb; 
}; 

class B

class B 
{ 
public: 
    B() 
    { 
     B** fb = a->getfb(); 

     *fb = this; 
    } 

    ~B() 
    { 
     B** fb = a->getfb(); 

     delete *fb;   // <--- stack overflow 

     *fb = NULL; 
    } 

private: 
    A* a; 
}; 

start()(一成員函數class C):

void C::start() 
{ 
    B** fb = a->getfb();  // 'a' is a pointer to an 'A' instance 

    if(*fb == NULL) 
     B* f = new B; 
} 

所以,每當我打電話start(),然後打電話~B(),我得到一個堆棧溢出!

+1

第一個問題:你知道什麼'delete'呢? –

+0

我認爲recurssion只發生在析構函數 – Jonas

+0

'exit'被遊戲結束時調用exit()時發生。基本上,它之後沒有任何東西(呃,......)被調用。 –

回答

7

聽起來沒錯。

fb的類型是B**所以*fb的類型是B*

所以,當你說delete *fb你正在調用class B的析構函數,這是一個遞歸調用,不會朝着基本情況進展,因此堆棧溢出。

6

B()分配*fb = this;然後:

~B() 
{ 
    ... 
    delete *fb; // delete this; 
    ... 
} 

..相當於調用delete this;析構函數~B()裏一遍又一遍,從而stackoveflow由於過多的遞歸函數。

這是Is it safe to delete this的一個很好的線程。

+0

那麼,那裏排名最高的回覆('刪除這個'通常被認爲是不好的形式,並且有難聞的氣味)的結論顯然是錯誤的。根據管理事務的方式,「刪除這個」可能是大多數刪除的最乾淨和最好的方式。 (這當然是最OO ---一個對象自己負責。) –

4

這並不奇怪。您在類B的析構函數內部,訪問當前正在調用其析構函數的B的SAME實例,然後再次刪除它。這將導致析構函數調用遞歸,從而導致堆棧溢出。由A類刪除fb足以清理你的記憶,在這裏你不想讓B類刪除自己。

你也不需要在B類構造函數中做你正在做的事情。你在A班的代碼是有道理的,你在B班有什麼危險和不必要的。在B班你沒有功能來設置其成員a。這意味着指針未初始化,然後嘗試B類的此實例分配給這個B類的成員指針未初始化A的實例

B() 
    { 
     B** fb = a->getfb(); // a never gets set to a meaningful value!!!!! 

     *fb = this; 
    } 
+0

沒有初始化'a',我很注意我只是想讓代碼儘可能清楚。我猜你是對的,我應該把所有的東西寫好。感謝你的回答。 – Jonas