再谈异常——谈C++与Object Pascal中的构造函数与异常

原创 2001年09月15日 11:45:00

再谈异常——谈C++与Object Pascal中的构造函数与异常

作者:Nicrosoft(nicrosoft@sunistudio.com) 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/n");
 return 1;
}

int A::Failed()
{
 return failed;
}

int main(int argc, char* argv[])
{
 A* a = new A;
 if ( a->Failed() )
  printf("failed/n");
 else
  printf("succeeded/n");

 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代码是全世界最优美的代码),编译器帮助程序员完成复杂的工作。两种语言都有存在的理由,都有存在的必要!而掌握它们之间的差别,能让你更好地控制它们,达到自由的理想王国。

c++构造函数中抛出异常的用途和用法

该实例用来帮助理解在构造函数中抛出异常的用途和用法。 它有三个文件构成,关键点处都给出了注释。 test.h, test.cpp,main.cpp $ cat test.h #ifndef __MYT...
  • hejinjing_tom_com
  • hejinjing_tom_com
  • 2016年01月18日 16:19
  • 1288

C++学习之构造函数中的异常处理

构造函数中可不可以抛出异常?当然可以。从语法上来说,是可以的;从实际情况来看,现在的软件系统日渐庞大和复杂,很难保证 Constructor 在执行过程中完全不发生一点异常。 那么,如果构造函数中抛...
  • lisong694767315
  • lisong694767315
  • 2015年03月15日 13:36
  • 2304

C++ 构造函数抛出异常会引起内存泄漏吗?

C++ 构造函数抛出异常会引起内存泄漏吗?   我们用实际代码说明问题:先看一下如下代码: #include  using namespace std; class Inner { public: I...
  • nodeathphoenix
  • nodeathphoenix
  • 2013年12月10日 22:57
  • 1675

C++构造函数和析构函数中抛出异常的注意事项

从语法上来说,构造函数和析构函数都可以抛出异常。但从逻辑上和风险控制上,构造函数和析构函数中尽量不要抛出异常,万不得已,一定要注意防止资源泄露。1.构造函数中抛出异常在C++构造函数中,既需要分配内存...
  • K346K346
  • K346K346
  • 2015年12月02日 10:40
  • 2615

构造函数应该尽量避免产生异常

构造函数应该尽量避免产生异常 1.说明        构造函数是对一个对象进行初始化,一般来说,构造函数不应该设计的太复杂,即只进行简单的成员变量的初始化即可, 这样就可以更好的保证构造函数不会产生异...
  • u011857683
  • u011857683
  • 2016年08月03日 20:18
  • 721

构造函数中尽量不要抛出异常,能避免的就避免。。不要在析构函数中抛出异常!

是否能在构造函数,析构函数中抛出异常?   最近在工作中,接触到两次这个问题,一次是与Manager的每月一次交流中,Manager问我这个问题,当时回答得支支吾吾;另外...
  • hzw05103020
  • hzw05103020
  • 2015年09月19日 13:31
  • 632

C++抛出异常与传递参数的区别

代码便已运行环境:VS2012+Debug+Win321.C++异常处理基本格式C++的异常处理机制有3部分组成:try(检查),throw(抛出),catch(捕获)。把需要检查的语句放在try模块...
  • K346K346
  • K346K346
  • 2015年11月29日 13:26
  • 1642

构造函数的异常处理

试分析推断下述代码的输出结果:#include #include #include using namespace std; void* operator new(size_t size) {...
  • u014161864
  • u014161864
  • 2015年03月02日 10:32
  • 4910

return C++构造函数的返回值

看到这个标题估计大家都很诧异,C++构造函数是没有返回值
  • happen23
  • happen23
  • 2014年11月01日 16:43
  • 2563

C++自定义异常类

1.C++程序中处理异常或者错误,可以使用异常机制,异常处理的结构为: try { //可能引发异常的代码 } catch( type e) { //异常处理 } catch(…) //省略号代表可以...
  • u011608357
  • u011608357
  • 2014年03月22日 22:36
  • 2634
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:再谈异常——谈C++与Object Pascal中的构造函数与异常
举报原因:
原因补充:

(最多只允许输入30个字)