在 C++ 中,如果一個類的建構函式丟擲異常(例如記憶體分配異常),我們應該如何處理,這是一個非常常見的問題。 考慮以下程式碼片段。 這裡 A 類的建構函式丟擲了一個異常。所以處理這種情況的最佳方法是在一個 try 塊中例項化 A。如果在 A 的建構函式中丟擲異常,i 將被堆疊展開銷燬,並且丟擲的異常將被捕獲...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
class MyException(string str){
private:
string msg;
public:
MyException(string str){
msg = str;
}
void printerrmsg(){
cout<<msg.c_str()<<endl;
}
}
class A{
private: int i;
//if exception is thrown in the constructor of A, i will de destroyed by stack unwinding
//and the thrown exception will be caught
A()
{
i = 10;
throw MyException(“Exception thrown in constructor of A()”);
}
};
void main(){
try{
A();
}
catch(MyException& e){
e.printerrmsg();
}
}
|
現在還有另外一個問題... 假設我們需要在建構函式中分配一些動態記憶體... 在執行此操作時,建構函式丟擲一個記憶體異常... 因此,引用基於堆的記憶體的基於堆疊的指標將被銷燬,因為堆疊展開... 所以我們將有一些沒有被任何指標引用的記憶體,因此我們無法訪問它... 所以很明顯這是一個記憶體洩漏... 那麼,我們如何處理這個問題...
在現代 C++ 中處理這種情況的最佳方法是使用 auto_ptr/shared_ptr... 所以解決方案看起來像下面這樣
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
|
#include <iostream>
#include <string>
#include <memory>
#include <boost/shared_ptr.hpp>
#include <boost/shared_array.hpp>
using namespace std;
class SomeClass{
public:
SomeClass(){}
~SomeClass(){};
};
typedef boost::shared_ptr<SomeClass> pSomeClass;
typedef boost::shared_ptr<char> pChar;
typedef boost::shard_array<char> charBuff;
class MyException{
public:
MyException(string str){
msg = str;
}
void printerrmsg(){
cout<<msg.c_str()<<endl;
}
private:
string msg;
};
class A{
private:
int i;
pChar m_ptrChar;
pSomeClass m_ptrSomeClass;
charBuff m_pcharBuff;
//if exception is thrown in the constructor of A, i will be destroyed by stack unwinding
//and the thrown exception will be caught
public:
A():m_ptrChar(new char),m_ptrSomeClass(new SomeClass),m_pcharBuff(new char[100])
{
i = 10;
throw MyException("Exception at A's constructor");
}
};
int main(){
try{
A objA;
}
catch(MyException& e){
e.printerrmsg();
}
return 1;
}
|
在 Symbian C++ 中,它透過一個叫做兩階段建構函式的概念來處理...(它之所以出現,是因為早期的 Symbian C++ 中沒有模板概念,因此沒有 auto_ptr)... 在這個過程中,如果我們想在堆上建立一個動態記憶體分配,並用 *pMem 指向它,那麼在構造的第一階段,我們將 *pMem 初始化為 NULL。 顯然這不會丟擲異常... 然後我們將這個 pMem 推入清理堆疊(這是 Symbian C++ 的一個新概念)... 並且在構造的第二階段,我們分配 pMem 指向的記憶體... 因此,如果建構函式失敗,我們仍然在清理堆疊中保留了 pMem 的引用... 我們只需要彈出它並銷燬它... 因此沒有記憶體洩漏的機會...