ロベールのC++入門講座でC++を初歩から入門する(4日目)
今日は「8章 ファイルとストリーム」から。
8章 ファイルとストリーム
c_strとdataはNULLターミネータされているか否かで違う
fstreamによるファイルの操作。この辺りは知っているつもり。
iostreamってistreamとostreamを多重継承したものなのね。ほほう。fstreamとstringstreamはiostreamを継承している。これは何となくわかるわ。
stream、buffering、flushの言葉の定義について。メモリ(バッファ)を介したファイルへの入出力をstreamと呼ぶらしい。分かってたつもりだけど、こう文章できちんと定義づけられると理解が深まる。
streamの流れを操作するものをマニュピュレータ(manipulator)と呼ぶらしい。試してみる。
/** * @file Manipurator.cpp */ #include <iostream> #include <iomanip> // マニュピュレータを使うために必要 using namespace std; int main() { char buf[] = "hello"; // printf("%02X", buf[count]); と同等の処理 for (int count = 0; count < sizeof(buf)/sizeof(*buf); count++) { cout << setw(2) << setfill('0') << hex << uppercase << (int)buf[count] << ' '; } cout << endl; return 0; }
実行結果は以下の通り。
% ./Manipurator 68 65 6C 6C 6F 00
名前空間について。また、namespaceを使えばエイリアスが与えられる。書式は以下の通り。
namespace <エイリアス名> = <名前空間名>;
9章 テンプレート
C++の非常に重要な機能、テンプレートについて。
Javaで言うところのジェネリクスという理解で合ってるかな。
まずは関数テンプレート。Cで同じことをやろうと思ったら関数マクロを使うしかないが、型の保証ができない。関数テンプレートはそこをきちんと保証できる。また、オーバライドも可能。
/** * @file Template1.cpp */ #include <iostream> #include <cstring> using namespace std; template <typename TYPE> TYPE Max(TYPE a, TYPE b) { cout << "Max<TYPE> is called" << endl; return (a > b) ? a : b; } // 特定の型に対してオーバーライドを行う template <> char* Max<char *>(char* a, char *b) { cout << "Max<char *> is called" << endl; return (strcmp(a, b) > 0) ? a : b; } template <> const char* Max<const char *>(const char* a, const char *b) { cout << "Max<const char *> is called" << endl; return (strcmp(a, b) > 0) ? a : b; } int main() { char str1[] = "chao"; char str2[] = "bye"; cout << Max(9, 3) << endl; cout << Max(-1.0, 2.5) << endl; cout << Max(str1, str2) << endl; cout << Max("good morning", (const char *)str1) << endl; return 0; }
% ./Template1 Max<TYPE> is called 9 Max<TYPE> is called 2.5 Max<char *> is called chao Max<const char *> is called good morning
関数テンプレートだけでなく、クラステンプレートも作れる。考え方は関数テンプレートとだいたい同じ。
クラステンプレートはデフォルト引数を取ることができる。
/** * @file Template5.cpp */ #include <iostream> using namespace std; template <typename TYPE = char> // デフォルト引数はchar class FortyTwo { public: TYPE Get() const { return 65; // ASCIIでは'A' }; }; int main() { FortyTwo<> ch; FortyTwo<int> i; cout << ch.Get() << endl; cout << i.Get() << endl; return 0; }
% ./Template5 A 65
10章 エラー処理と例外
goto文
「goto文は絶対悪ではない」という主張は同意。特にCなら例外的な処理はgoto err;してラベル先で処理を記述するなんてことはよくある。setjmp()、longjmp()とかの方がよっぽとアレ。
例外
- tryブロック内で例外を発生させる(throwさせる)と対応するcatchへ飛ぶ。
- その関数でcacthされなかった例外は、外の関数へ投げられる。最後までcatchできなければエラー終了。
- catch(...)で全ての例外をcacthできる。
- catch節からもthrowで例外を投げられる。ただし外のtry-catchに対して投げることになる。
- catchした例外と同じ例外を投げる場合は、単にthrow;と書く。
/** * @file Execption4b.cpp */ #include <iostream> using namespace std; void Error() { int *p = NULL; try { p = new int(10); throw "エラー発生!"; delete[] p; } catch(...) { cerr << "メモリを解放します。" << endl; delete[] p; throw; // "エラー発生!"がそのままthrowされる } } int main() { try { Error(); } catch(const char* error) { cerr << error << endl; } return 0; }
% ./Exception4b メモリを解放します。 エラー発生!
- 例外は参照でも受け取れる。オブジェクトをthrow & catchすることも可能。そもそも標準例外クラスというものがある。
- コンストラクタから外へ例外が投げられた場合、そのクラスのデストラクタは呼ばれない。コンストラクタ内でメモリ確保を行っている場合は注意。
次は「11章 もっと高く」から。