2011-12-06 34 views
2

假設我有一個下面的構造函數在C++類:投擲用C異常++時解放出來的本地緩存

MyClass::MyClass() 
{ 
    char* buffer = malloc(100); 
    if (0 != someD3DXCallThatCanFail(..., buffer, ...)) 
    { 
     free(buffer); 
     throw MyException(L"some message"); 
    } 
    char* buffer2 = malloc(200); 
    if (0 != anotherD3DCallThatCanFail(..., buffer2, ...)) 
    { 
     free(buffer); 
     free(buffer2); 
     throw MyException(L"another message"); 
    } 
    .. more code here where buffer and buffer2 are still used 

    free(buffer); 
    free(buffer2); 
} 

編輯:我不討厭的malloc /免費新/刪除,但不幸的是我需要使用緩衝區來加載紋理然後傳遞到ID3D10ShaderResourceView,ID3D10Buffer,頂點緩衝區等。所有這些都需要指向緩衝區的指針。

我正在嘗試使用異常而不是返回錯誤代碼。 我也希望在需要時創建緩衝區,並在不再需要它們時立即釋放它們。

但是,看起來很醜陋的是,如果發生錯誤,無論我返回錯誤代碼還是拋出異常,我仍然應該記住清理在此之前創建的任何緩衝區。如果我有10個緩衝區和10個可能的錯誤點,我將不得不調用free()100次(在每個錯誤情況下記住釋放每個緩衝區)。

現在假設我或者更糟,我的同事想要改變一些邏輯,比如說在中間的某個地方添加另一個緩衝區。現在,他必須查看通過該方法其餘部分可能發生的所有錯誤,並在每個這樣的地方爲該緩衝區添加free()。如果他很匆忙,他可以輕鬆地忽略幾個這樣的地方,並且你有內存泄漏。

這也使得代碼非常龐大。

finally關鍵字可以解決Java或C#中的問題。無論代碼在哪裏發生異常,我仍然會在「finally」中清除所有這些緩衝區(順便說一句,你不需要垃圾回收)。在我所瞭解的C++中,我可能必須爲任何這樣的緩衝區創建一個成員變量,並且在析構函數中確保緩衝區被清除。看起來對我來說也很難看,因爲一個名爲「pBuffer」的成員變量,即使是私有成員變量也僅僅是垃圾,因爲它只用於一個方法(在這種情況下是構造函數),並且將始終爲NULL,剩下的時間。

必須是一個常見問題,但我沒有設法使用搜索找到答案。謝謝!

+4

除非絕對必要,否則應該避免在C++中使用'malloc/free'的習慣。優先使用'new/delete'。 –

+2

確實,但不希望使用'new/delete' :)使用容器,智能指針等來避免手動管理內存。他們使保證異常安全變得更容易。 –

+1

@Dave - 一旦你這樣做,避免使用智能指針'new'和'delete'來獲得更簡單的生活 –

回答

8

停止手動管理內存,您不會遇到這些問題。使用類似std::vector<char>的東西。

或者,你可以使用類似Boost的shared_array,但是這是矯枉過正你想在這裏做什麼:

http://www.boost.org/doc/libs/1_41_0/libs/smart_ptr/shared_array.htm

在這裏所做的更普遍的觀點是,你應該使用RAII習語 - 也就是說,當你獲得資源時,你將它們存儲在一個類的實例中,這個類的析構函數再次釋放它們。然後,無論擁有資源的類的實例是否超出範圍,都將保證資源被釋放。

在這裏看到:

http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization

4

規範的答案是「資源獲取初始化」(RAII)和智能指針的原理。您可以在堆棧上創建一個類實例,以釋放內存在其析構函數中,例如boost::scoped_ptr

+0

謝謝,我現在通過你和其他人的回答理解了RAII的想法)) – iseeall

1

對此使用慣用的C++方法:RAII。這個維基百科頁面有C++示例。

1

再次搜索「C++智能指針」。你需要一個內存異常安全的包裝,而不是原始的malloc,正如你所指出的那樣帶來了很多令人頭痛的問題,並且順便說一句,現在你可以用operator new代替,現在你正在編寫C++而不是C代碼。

先前的答案here涵蓋此區域。

2

相反,使用raii

MyClass::MyClass() 
{ 
    std::vector<char> buffer(100); 
    if (0 != someD3DXCallThatCanFail(...)) 
    { 
     throw MyException(L"some message"); 
    } 
    std::vector<char> buffer2c(200); 
    if (0 != anotherD3DCallThatCanFail(...)) 
    { 
     throw MyException(L"another message"); 
    } 
    .. more code here 
} 
1

的規範答案,這將在的unique_ptr C++ 11。在此之前,單身人士可能需要scoped_ptr(http://www.boost.org/doc/libs/1_47_0/libs/smart_ptr/scoped_ptr.htm),而scoped_array(http://www.boost.org/doc/libs/1_47_0 /libs/smart_ptr/scoped_array.htm)數組,均來自Boost。或者你可以自己編寫相應的代碼。