@uents blog

Code wins arguments.

ロベールの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

vectorクラステンプレートについて。突然STLが出てくるのね(^^; STLを勉強するならまた別の本がいるな。

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章 もっと高く」から。