再談異常――談C++與Object Pascal中的構(gòu)造函數(shù)與異常
作者:Nicrosoft(nicrosoft@sunistudio.com) 2001.9.15
個(gè)人主頁(yè):http://www.sunistudio.com/nicrosoft/
東日文檔:http://www.sunistudio.com/asp/sunidoc.asp
我們知道,類的構(gòu)造函數(shù)是沒有返回值的,如果構(gòu)造函數(shù)構(gòu)造對(duì)象失敗,不可能依靠返回錯(cuò)誤代碼。那么,在程序中如何標(biāo)識(shí)構(gòu)造函數(shù)的失敗呢?最“標(biāo)準(zhǔn)”的方法就是:拋出一個(gè)異常。
構(gòu)造函數(shù)失敗,意味著對(duì)象的構(gòu)造失敗,那么拋出異常之后,這個(gè)“半死不活”的對(duì)象會(huì)被如何處理呢?這就是本文的主題。
在C++中,構(gòu)造函數(shù)拋出異常后,析構(gòu)函數(shù)不會(huì)被調(diào)用。這是合理的,因?yàn)榇藭r(shí)對(duì)象并沒有被完整構(gòu)造。也就是說,如果構(gòu)造函數(shù)已經(jīng)做了一些諸如分配內(nèi)存、
打開文件等操作的話,那么類需要有自己的成員來記住做過哪些動(dòng)作。在C++中,經(jīng)典的解決方案是使用STL的標(biāo)準(zhǔn)類auto_ptr,這在每一本經(jīng)典
C++著作中都有介紹,我在這里就不多說了。在這里,我想再介紹一種“非常規(guī)”的方式,其思想就是避免在構(gòu)造函數(shù)中拋出異常。我們可以在類中增加一個(gè)
Init(); 以及 UnInit();成員函數(shù)用于進(jìn)行容易產(chǎn)生錯(cuò)誤的資源分配工作,而真正的構(gòu)造函數(shù)中先將所有成員置為NULL,然后調(diào)用
Init(); 并判斷其返回值(或者捕捉 Init()拋出的異常),如果Init();失敗了,則在構(gòu)造函數(shù)中調(diào)用 UnInit();
并設(shè)置一個(gè)標(biāo)志位表明構(gòu)造失敗。UnInit()中按照成員是否為NULL進(jìn)行資源的釋放工作。示例代碼如下:
class A
{
private:
char* str;
int failed;
public:
A();
~A();
int Init();
int UnInit();
int Failed();
};
A::A()
{
str = NULL;
try
{
Init();
failed = 0;
}
catch(...)
{
failed = 1;
UnInit();
}
}
A::~A()
{
UnInit();
}
int A::Init()
{
str = new char[10];
strcpy(str, "ABCDEFGHI");
throw 10;
return 1;
}
int A::UnInit()
{
if (!str)
{
delete []str;
str = NULL;
}
printf("Free Resource
");
return 1;
}
int A::Failed()
{
return failed;
}
int main(int argc, char* argv[])
{
A* a = new A;
if ( a->Failed() )
printf("failed
");
else
printf("succeeded
");
delete a;
getchar();
return 0;
}
你會(huì)發(fā)現(xiàn),在int A::Init()中包含了throw 10;的代碼(產(chǎn)生一個(gè)異常,模擬錯(cuò)誤的發(fā)生),執(zhí)行結(jié)果是:
Free Resource
failed
Free Resource
雖然 UnInit();被調(diào)用了兩次,但是由于UnInit();中做了判斷(if (!str)),因此不會(huì)發(fā)生錯(cuò)誤。而如果沒有發(fā)生異常(去掉 int A::Init()中的throw 10;代碼),執(zhí)行結(jié)果是:
Succeeded
Free Resource
和正常的流程沒有任何區(qū)別。
在Object Pascal(Delphi/VCL)中,這個(gè)問題就變得非常的簡(jiǎn)單了,因?yàn)?OP
對(duì)構(gòu)造函數(shù)的異常的處理與C++不同,在Create時(shí)拋出異常后,編譯器會(huì)自動(dòng)調(diào)用析構(gòu)函數(shù)Destroy,并且會(huì)判斷哪些資源被分配了,實(shí)行自動(dòng)回
收。因此,其代碼也變得非常簡(jiǎn)潔,如下:
type
A = class
private
str : PChar;
public
constructor Create();
destructor Destroy(); override;
end;
constructor A.Create();
begin
str := StrAlloc(10);
StrCopy(str, 'ABCDEFGHI');
raise Exception.Create('error');
end;
destructor A.Destroy();
begin
StrDispose(str);
WriteLn('Free Resource');
end;
var oa : A;
i : integer;
begin
try
oa := A.Create();
WriteLn('Succeeded');
oa.Free();
except
oa := nil;
WriteLn('Failed');
end;
Read(i);
end.
在這段代碼中,如果構(gòu)造函數(shù)拋出異常(即Create中含有raise Exception.Create('error');),執(zhí)行的結(jié)果是:
Free Resource
Failed
此時(shí)的“Free Resource”輸出是由編譯器自動(dòng)調(diào)用析構(gòu)函數(shù)所產(chǎn)生的。而如果構(gòu)造函數(shù)正常返回(即不拋出異常),則執(zhí)行結(jié)果是:
Succeeded
Free Resource
此時(shí)的“Free Resource”輸出是由 oa.Free()的調(diào)用產(chǎn)生的。
綜上,C++與Object
Pascal對(duì)于構(gòu)造函數(shù)拋出異常后的不同處理方式,其實(shí)正是兩種語(yǔ)言的設(shè)計(jì)思想的體現(xiàn)。C++秉承C的風(fēng)格,注重效率,一切交給程序員來掌握,編譯器不
作多余動(dòng)作。Object
Pascal繼承Pascal的風(fēng)格,注重程序的美學(xué)意義(不可否認(rèn),Pascal代碼是全世界最優(yōu)美的代碼),編譯器幫助程序員完成復(fù)雜的工作。兩種語(yǔ)
言都有存在的理由,都有存在的必要!而掌握它們之間的差別,能讓你更好地控制它們,達(dá)到自由的理想王國(guó)。
posted on 2009-05-20 00:42
幽幽 閱讀(653)
評(píng)論(1) 編輯 收藏 引用 所屬分類:
雜集