C++ 类构造函数初始化列表的异常机制 function-try block

       上篇文章提到了 C++ 类构造函数对成员变量初始化赋值时使用“初始化列表”的方式带来的好处。这篇文章将介绍另一个大大的好处——由初始化列表异常块机制带来的构造函数的安全性机制。

       听起来有些拗口。直接上代码:

class Fck { int* sbArray; public: Fck ( int sbNum ); ~Fck (); }; Fck :: Fck ( int sbNum ) try: sbArray ( new int[sbNum] ) { cout << " Fck Constructing " << sbNum << endl; } catch ( bad_alloc& err ) { cout << err.what() << endl; cout << " Fck Failed " << endl; }; Fck :: ~Fck () { delete this->sbArray; cout << " Fck Destructing " << endl; } int _tmain(int argc, _TCHAR* argv[]) { Fck fck ( 5 ); //Fck fck_1 ( -1 ); try { Fck fck_1 ( -1 ); } catch ( bad_alloc& err ) { cout << err.what() << endl; cout << " Main Failed " << endl; } catch ( ... ) { } system ( "pause" ); return 0; } 

       《C++ 异常机制 32 点》这篇文章里第 20 点谈到了这种用法。Fck 类的构造函数中需要对类成员变量 int* 型的 sbArray 指定一块新开辟出来的堆内存空间。这里有一个问题,若传递进去的 sbNum 是负值当如何?当然会报异常。注意到构造函数后紧跟着一个 catch 块用于捕获初始化列表 try 块生成的异常。完了么?没有,尽管原代码没有写,但是,在执行完这段 catch 块之后,会紧跟着再将此异常“外抛”出去!这个通过查看汇编码就知道了,在该 catch 块完成后,系统会自动再调一次 throw,在此就不展开细说了,读者可自行验证。这有点像 JAVA 里面的“上抛机制”。抛给谁呢?当然是给主函数了,于是,主函数必须还得有一个 try – catch 块去处理异常。

       这么麻烦?这样做是有很大的好处的。试想,若成员构造时发生了异常,此时这个类对像是构造失败的,一个构造失败的类对像是不可以使用的。构造函数必须通知主函数自己是否正常。这一机制保证了这一过程的安全。

       你当然也可以这么来写构造函数,即不使用初始化列表的方式:

Fck :: Fck ( int sbNum ) { try { sbArray = new int[sbNum]; } catch ( ... ) {} cout << " Fck Constructing " << sbNum << endl; } 

       这样,异常在构造函数内处理完了,不会再“上抛”,主函数根本就不知道这个类对像是构造失败的,使用中必然会出现不可预知的错误!


       若有读者对此仍存疑,或者想了解更多,请参阅《续:为何说 C++ 构造函数初始化列表异常机制是必要的》


参考:

《C++ 0x(C++ 09)新标准全部革新提案文档列表》

你可能感兴趣的:(C++)