Chiharu の日記

絵描き C/C++ プログラマーの日記です。

コンストラクタで例外はアリか? 〜私はアリ派

ネタ元: わんくま東京勉強会#56 懇親会ふぉろー
えー。とりあえず C++ に限定して、私はアリ派。
確かに、下記のようなクラス定義はまずいですが…、

class FileReader {
public:
 FileReader(const char* iFileName):mFile(fopen(iFileName, "r")),
  mBuffer(4096) { // mBuffer が std::bad_alloc 送出の可能性あり
 }
 ~FileReader()
 {
  if (mFile != nullptr) {
   fclose(mFile);
  }
 }
private:
 FILE* mFile;
 std::vector<char> mBuffer;
};
int main()
{
 try {
  FileReader aFile("test.dat");
 } catch (...) {
  // aFile::mFile がリソース リーク
 }
}

例外安全が問題なら、C++ では RAII が有効なんだから、それを最大限活用すればよいのでは?

class FileReader {
public:
 FileReader(const char* iFileName):mFile(fopen(iFileName, "r"),
  [](FILE* iFile) -> void { if (iFile) fclose(iFile); }),
  mBuffer(4096) { // mBuffer が std::bad_alloc 送出の可能性あり
 }
 ~FileReader()
 {
  // do nothing.
 }
private:
 std::shared_ptr<FILE> mFile;
 std::vector<char> mBuffer;
};
int main()
{
 try {
  FileReader aFile("test.dat");
 } catch (...) {
  // aFile::mFile は解放済
 }
}

そもそも、コンストラクタの例外送出を否定するということは、RAII の否定にもつながるように感じてしまいます。(飛躍しすぎ?
例えば C++0x の std::shared_ptr ではコンストラクタでデリーターとオブジェクト ホルダーの双方を new しますが、new には当然ながら std::bad_alloc 送出の可能性がついてまわります。stl のコンテナだってコンストラクタに要素数を指定すれば std::bad_alloc 送出の可能性が出てきます。
例外送出 NG を一貫すると、C++ 標準ライブラリさえ使用できなくなってしまいます。
んー。RAII がある処理系でコンストラクタからの例外を否定する理由があるとすれば、『例外送出するコンストラクタを持つクラスは、全てのメンバが RAII 有効なオブジェクトでなければ例外安全の確保が難しい』ということくらいでしょうか。例外安全の確保の難易度が高いと。ゆえに、例外安全の精度が実装者に依存してブレが出やすいと。担当者のスキルのブレで不具合を発生させやすいと。そうした側面はなくはない、かな。でも、こういう側面って C++ 全般に言えますよね。十分に言語仕様に精通していないと、安全で必要十分なコードを書けないというか。まぁ、それは別の話。
話を戻して。逆に、RAII がない処理系だと、例外発生時に呼び出し元からインスタンスを参照できないとエラー処理できないので、コンストラクタからの例外は NG な気はしますけれど。んー。あ。考えてみると、それもコンストラクタ内部で try & catch & 再 throw すればいい話な気がしてきました。ふーむ。
―――というか、RAII の観点からリソース確保に失敗した不完全なインスタンスを呼び出し元から参照可能という状況の方が気持ち悪い感じがします。あー。そうすると、コンストラクタの例外 NG というコーディング規約だと、コンストラクタでリソース確保しないのか。
ひとつ思い出しました。最初の職場が例外禁止で、obj::ctor + obj::isComplete? または obj::ctor + obj::init を強制されていましたけれど、obj::isComplete または obj::init の呼び忘れでよく不具合出してました。その経験から、RAII って大事と学んだ次第です。
いずれにしても、RAII 有効な環境でコンストラクタの例外送出 NG の決定打。あれば知りたいところです。
(補足)
例外安全上、例外送出 NG なのはデストラクタ、swap、例外クラスの全メンバで FA だと思ってます。