「詳説C++」 大城正典 著

詳説C++ 第2版 (C MAGAZINE)
いやー、時間がかかった。他の本の2,3倍時間がかかったかもしれない。分からないまま流し読みで済ませたところも結構ある。それでも読んだ価値があったような気がする。これ読んでからちょこっと「Effective C++」の気になる箇所を見たらすんなり理解できるようになっていた。
結構厚めの本だけど、それでも詳細を省いている機能などもある。C++って思ってたよりずっと深いなあ。


少し印象に残ったところを。

つまり、
 cout << endl;
は、効果の面ではANSI Cで記述した、
 printf("%c",'\n');
に等しいといえます。では、endlは文字定数'\n'なのでしょうか。
 実は、endlは関数なのです。標準C言語およびC++では、関数名をただ書けば、それはその関数へのポインタとして扱われます。

endlは関数だったのか。こういう使い方はうまく応用すれば便利かもしれない。

汎用性の高い関数の引数を意味的に分類してみると、
(1)処理対象の情報を指定する引数
(2)働きの細部を決める補助的な引数
の2種類に分けることができます。汎用性の高い関数は、(2)タイプの引数を1個以上もっているので、汎用性のないバージョンよりも引数が多くなるのです。

C++と直接関係ないけど、やっぱりそうなんだよな。当たり前のことではあるけど、今まで明確に意識してなかった。C++の場合はデフォルト実引数という仕組みで少しは引数が減らせる。

手続き(関数)がほかの手続き(関数)を呼び出しています。その過程において、関数の間で情報(データ)がやり取りされていきます。このような動作原理によって処理が行われるプログラム言語を、手続き型言語(precedural language)といいます。C言語のように手続きが関数の形をとっている場合は、関数型言語(functional language)と呼びます。

関数型言語というとLispHaskellのような名前を思い浮かべるけど、違う意味で使う場合もあるんだな。意味があいまいになってしまうのは専門用語にありがちなことだ。
関数型言語 - Wikipedia

typeid演算子を使った型識別は、便利な機能です。しかし、この機能を使うと、けっきょくは識別した型によって処理を分岐する、List2-10のようなプログラムを作成してしまうことになります。Level2.3で見たように、これはプログラムの硬直化につながります。継承と仮想関数を使って、多相性を十分生かすようにプログラムを設計すれば、たいていの場合はtypeid演算子を使用する必要はありません。

なるほど。言われてみればその通りだ。便利そうな機能はとりあえず使ってみたくなるので気をつけないといけない。

ロケールの機能は、文字コードの変換や、数値・金額・時間の表現など、多岐にわたります。Fig.12-2に示すように、これらの機能はいくつかのカテゴリに分類されています。
 たとえば、文字の種類・文字コードに関する機能を集めた文字カテゴリ、数値の表現に関する機能を集めた数値カテゴリなどです。各カテゴリに属する機能はさらに細かく分類されて、ファセット(facet,特性項目)という単位に分類されます。facetとは、英語で「切り口の面、ある観点から見たものごとの様相・側面」といった意味があります。
 ファセットは、facetというクラスの派生クラスとして定義されており、実際にはファセット型のオブジェクトが各機能を提供します。

ロケール自体にはあまり興味はないのだけど、facetクラスというのが気になる。2次元の表を実装するパターンとして使えるようなものだろうか。

is-a関係、has-a関係

「is-a関係、has-a関係を混同するな」という話はよく聞くのだけど、今までは「そんなの全然違うんだから間違えねぇよ」と思っていた。思い上がりだった。

classA <---(継承)--- classB
classC <>---(包含)--- classD

こんな関係があったとして、簡単にするためにすべてのメソッドとメンバがpublicだとすると、classBのインスタンスはclassAのメンバやメソッドを使える。classCはclassDのメソッドとメンバがすべて使える。つまり、意味を考えずに実行することだけ考えるなら、is-a関係とhas-a関係は入れ替え可能だ。

コンテナの初期化

最初からコンテナに入れたい初期値となるデータがある場合はどうやるのが効率がいいんだろう。コンストラクタの定義を見ると、値を指定した場合すべての要素が同じその値で初期化されてしまうようだ。別のコンテナからコピーしてくる方法はあるようだけど、それだとコピー元のコンテナをまず初期化しなければいけないので結局問題は解決されない。配列のように初期化で値が書けるといいんだけど。


STL標準講座―標準テンプレートライブラリを利用したC++プログラミング (Programmer’s SELECTION)
STL標準講座」という本を見てみると、配列をリストにコピーする例が書いてある。これだ。
しかし、copy()の第2引数はend()の要素、つまりコンテナの最後の要素の次を入れるようになっている。このコードでも10個の要素を持った配列numsを用意し、copyの第2引数には&nums[9]を入れている。そしてその結果はnums[8]までがコピーされている。最後の要素はコピーされないのか。まあ、ひとつ余分な最後を表す要素を入れておけばいいので、かまわないと言えばかまわないのでけど、なんとなくすっきりしないかな……


C++ プライマー 第4版 IT Architect’ Archive クラシックモダン・コンピューティング (IT Architects’ Archive―CLASSIC MODERN COMPUTING)
と思って今度は「C++プライマー」という本を見てみると、ちゃんと初期化について書かれている。さすが厚いだけあるぜ。

ポインタはイテレータであるから、組み込み型配列を指す一対のポインタでコンテナを初期化できても驚くには当たらない。


char *words[] = {"stately", "plump", "buck", "mulligan"};
// wordsの要素数を計算する
size_t words_size = sizeof(words)/sizeof(char *);
// 配列全体を使ってwords2を初期化する
list words2(words, words + words_size);


ここで、sizeof(5.8節、p.193)を使って配列の大きさを計算している。配列の先頭を指すポインタと、出外れポインタをアーギュメントとして渡している。

「驚くに当たらない」という微妙に場違いな感じのする語感が好きだ。
それは置いておいて、出外れポインタって何? イテレータのend()が指すところと同じような、最後の要素の次を指すポインタだろうと想像はつくけど、一般的な言葉なのかな? そして配列でもそれはありなのか?


この本の「配列とポインタ」の章にはこうある。

ポインタ算術が合法なのは、元のポインタと新しく求めたポインタが同じ配列の中の要素を指しているか、出外れポインタであるときに限る。

配列あるいは一般にオブジェクトの直後のアドレスを求めることは合法である。そういうアドレスを持つポインタを参照剥がしすることは違法である。また、直後よりさらに先のアドレス、または、配列の先頭より手前のアドレスを求めるのも違法である。

ああ、そういえば微かな記憶が。Cの知識だな。今手元にはC++の本しかないのだけど、これらを探してもこの事は書いてなかった(もちろん探し方が悪いのかもしれない)。
そうか。イテレータのend()みたいなのはポインタ計算でもあったのか。そういえばあったよなあ。すっかり記憶から消えていたけど。


Modern Classics Ulysses (Penguin Modern Classics)
ところでさっきの例題に出てきた文字列、"stately", "plump", "buck", "mulligan"ってなんだろうと思って調べてたら、ジェイムズ・ジョイスの「ユリシーズ」の冒頭だった。なぜここに? ずいぶん高尚だな。
"Stately, plump Buck Mulligan came from the stairhead, bearing a bowl of lather on which a mirror and a razor lay crossed."