程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> 再談異常——談C++與ObjectPascal中的構造函數與異常

再談異常——談C++與ObjectPascal中的構造函數與異常

編輯:Delphi

再談異常——談C++與Object Pascal中的構造函數與異常

作者:Nicrosoft([email protected]) 2001.9.15
個人主頁:http://www.sunistudio.com/nicrosoft/
東日文檔:http://www.sunistudio.com/asp/sunidoc.asp

  我們知道,類的構造函數是沒有返回值的,如果構造函數構造對象失敗,不可能依靠返回錯誤代碼。那麼,在程序中如何標識構造函數的失敗呢?最“標准”的方法就是:拋出一個異常。

  構造函數失敗,意味著對象的構造失敗,那麼拋出異常之後,這個“半死不活”的對象會被如何處理呢?這就是本文的主題。

  在C++中,構造函數拋出異常後,析構函數不會被調用。這是合理的,因為此時對象並沒有被完整構造。也就是說,如果構造函數已經做了一些諸如分配內存、打開文件等操作的話,那麼類需要有自己的成員來記住做過哪些動作。在C++中,經典的解決方案是使用STL的標准類auto_ptr,這在每一本經典C++著作中都有介紹,我在這裡就不多說了。在這裡,我想再介紹一種“非常規”的方式,其思想就是避免在構造函數中拋出異常。我們可以在類中增加一個 Init(); 以及 UnInit();成員函數用於進行容易產生錯誤的資源分配工作,而真正的構造函數中先將所有成員置為NULL,然後調用 Init(); 並判斷其返回值(或者捕捉 Init()拋出的異常),如果Init();失敗了,則在構造函數中調用 UnInit(); 並設置一個標志位表明構造失敗。UnInit()中按照成員是否為NULL進行資源的釋放工作。示例代碼如下:
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;
}

  你會發現,在int A::Init()中包含了throw 10;的代碼(產生一個異常,模擬錯誤的發生),執行結果是:
  Free Resource
  failed
  Free Resource
  雖然 UnInit();被調用了兩次,但是由於UnInit();中做了判斷(if (!str)),因此不會發生錯誤。而如果沒有發生異常(去掉 int A::Init()中的throw 10;代碼),執行結果是:
  Succeeded
  Free Resource
  和正常的流程沒有任何區別。

  在Object Pascal(Delphi/VCL)中,這個問題就變得非常的簡單了,因為 OP 對構造函數的異常的處理與C++不同,在Create時拋出異常後,編譯器會自動調用析構函數Destroy,並且會判斷哪些資源被分配了,實行自動回收。因此,其代碼也變得非常簡潔,如下:
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.

  在這段代碼中,如果構造函數拋出異常(即Create中含有raise Exception.Create(error);),執行的結果是:
  Free Resource
  Failed
  此時的“Free Resource”輸出是由編譯器自動調用析構函數所產生的。而如果構造函數正常返回(即不拋出異常),則執行結果是:
  Succeeded
  Free Resource
  此時的“Free Resource”輸出是由 oa.Free()的調用產生的。

  綜上,C++與Object Pascal對於構造函數拋出異常後的不同處理方式,其實正是兩種語言的設計思想的體現。C++秉承C的風格,注重效率,一切交給程序員來掌握,編譯器不作多余動作。Object Pascal繼承Pascal的風格,注重程序的美學意義(不可否認,Pascal代碼是全世界最優美的代碼),編譯器幫助程序員完成復雜的工作。兩種語言都有存在的理由,都有存在的必要!而掌握它們之間的差別,能讓你更好地控制它們,達到自由的理想王國。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved