| 網(wǎng)站首頁 | 關于我們 | 開發(fā)優(yōu)勢 | 產品展示 |
| 合作企業(yè) | 新聞動態(tài) | 聯(lián)系我們 | 電話聯(lián)系 |
文章作者:濟南軟件開發(fā) 時間:2016年12月20日
如果構造函數(shù)內發(fā)生異常,已經(jīng)分配的資源是不會自動釋放的,比如
class B{
public:
B(){
printf("into B constructor\n");
}
~B(){
printf("into B destructor\n");
}
};
class C{
public:
C(){
printf("into C constructor\n");
throw std::runtime_error(" exception from C constructor");
}
~C(){
printf("into C destructor\n");
}
};
class A{
public:
A(){
printf("into A constructor \n");
}
~A(){
printf("into A destructor \n");
}
};
class D:A{
public:
D():A(), b(NULL),c(NULL) {
printf("into D constructor\n");
b = new B();
c = new C();
}
~D(){
printf("into D destructor\n");
delete b;
delete c;
}
private:
B *b;
C *c;
};
int main(int argc, char **argv){
D d;
return 0;
}
運行結果如下:
into A constructor
into D constructor
into B constructor
into C constructor
terminate called after throwing an instance of 'std::runtime_error'
what(): exception from C constructor
對象c在構造過程中拋出異常,指針b指向的內存空間不會被釋放。
如何釋放b的內存呢?首先我們可以看出c++是不會調用析構函數(shù)的,因為析構函數(shù)不知道對象已經(jīng)構造了多少,哪些資源該釋放,哪些不該釋放,當然編譯器可以記錄這些內容,但是會嚴重影響效率。另外在語義上,c++認為,對象的生命周期是構造函數(shù)正常結束至析構函數(shù)結束之間,構造不完全的對象是一個沒有生命的東西,是不存在的,你不能對一個不存在的對象調用析構函數(shù)。編譯器默認會做的只是釋放對象d的內存空間。對象b指向的堆內存可以通過使用異常顯示釋放
D():A(), b(NULL), c(NULL){
printf("into D constructor\n");
try{
b = new B();
c = new C();
}catch(std::runtime_error &e){
printf("into D constructor catch \n");
delete b; b=NULL;
delete c; c=NULL;
}
運行結果如下:
into A constructor
into D constructor
into B constructor
into C constructor
into D constructor catch
into B destructor
into D destructor
into A destructor
b被正常釋放了,我們再做一下改變,將b和c的構造放到初始化列表中做,將D的構造函數(shù)改成下面這樣,
D::D() : A(),b(new B()),c(new C())
{
}
我們繼續(xù)使用異常,會不會有效?
D() try:A(), b(new B()), c(new C()) {
printf("into D constructor\n");
}catch(std::runtime_error &e){
printf("in D constructor catch: %s \n", e.what());
cleanup();
}
看上去very nice啊,跑一下試試,
into A constructor
into B constructor
into C constructor
into A destructor
in D constructor catch: exception from C constructor
into B destructor
into C destructor
*** glibc detected *** ./a.out: free(): invalid pointer: ***
指針錯誤!同時我們可以發(fā)現(xiàn)在進入catch語句前基類A執(zhí)行了析構函數(shù),這說明到達catch語句時,已經(jīng)跳出了構造函數(shù)的范圍,D和A的成員數(shù)據(jù)都已經(jīng)是不可訪問的了。
C++為什么要這樣做呢,為什么構造函數(shù)體外的catch語句中無法訪問數(shù)據(jù)成員?
首先,無法知道b和c是否已經(jīng)初始化了,刪除未初始化指針是非法的。
其次,我們假設可以知道b初始化了,c沒有初始化,我們要刪除b,如果catch中可以訪問b和c的話(我們做個假設),改變B的類型,使B包含一個A*成員數(shù)據(jù),考慮下這種情況,
D() try:A(), b(new B(static_cast<A*>(this))), c(new C()) {
printf("into D constructor\n");
}catch(std::runtime_error &e){
printf("in D constructor catch: %s \n", e.what());
cleanup();
}
A已經(jīng)析構了,b的數(shù)據(jù)成員A已經(jīng)不存在,這是很危險的。
c++認為,不管是基類還是數(shù)據(jù)成員構造出現(xiàn)失敗,那么整個對象構造都是失敗的,不存在半成品。構造函數(shù)塊外的catch語句(上面這種)即使沒有顯示地重新拋出異常,c++也會自動拋出,一直到最下層派生類對象構造處停住。比如以上例子,catch沒有顯示throw,在main函數(shù)里:
try{
D d;
}catch (std::runtime_error &e){
printf("int main: %s\n", e.what());
}
仍然會捕捉到C構造函數(shù)拋出的runtime_error異常。
綜上,可以總結以下規(guī)則
1. 構造函數(shù)體外的try-catch語句只有一個用途——轉換捕捉到的異常對象
2. 必須在構造函數(shù)體內分配資源,不要在初始化列表中
3. 一定要用try-catch語句管理資源的話,在構造函數(shù)體內。
4. 使用RAII方式管理資源,這會少死亡很多腦細胞,像這樣子
class D{
auto_ptr<B> b;
auto_ptr<C> c;
}
D::D() : A( ),b(new B()), c(new C())
{
}
想要了解更多詳情歡迎來電咨詢18678812288
登陸網(wǎng)址:m.h6244.cn。
聯(lián)系人:王經(jīng)理。