- まずはこの章をまるごと覚えよう
- vector/listコンテナクラス、操作法、反復子(イテレータ)、アルゴリズム、関数オブジェクトまで、一通り出てくる
- STLは、よく使うだろう処理を作っておいてくれて、それを使う人は楽チンな使い方をしてよいようにした「サービスの塊」のようなもの。したがって、「どれは楽チンに仕立てられているのか」「楽チンに使うということは、指定された使い方に従うことを要請される」という2点を覚えておけばよい
- 以下、「なには楽チン」「どう使うように強制」、の2つの視点で考える
- コンテナクラス
- データの持ち方、出し入れなどにより、異なる仕様でdeque,list,map,multipam,priority_queue,set,stack,vectorがある
- 任意の型を要素として持てるようにしてあり、そのハンドリング方法が定義分けしてある、という感じ
- 型を指定するときはtypedef
- size_type,reference,const_reference,iterator,const_iterator,reverse_iterator,const_reveres_iterator,value_type,allocator_type,key_type,key_compare,value_compare
- データの持ち方の2大別
- まずはvectorコンテナ
- 使い方を工夫する余地はあるけれど、基本の使い方で十分、と思うのなら(そしてそう思っている)
- まずは「作成方法」、vectorクラスのコンストラクタが以下のような作成を定義している、ということ
#include <vector>
vector<int> iv;
vector<char> cv(20);
vector<char> cv2(5,'x');
vector<int> iv2(iv);
-
- vectorはクラスなのでメンバー関数がある。vectorオブジェクトにはメンバー関数を作用させることができる、比較演算子もある
- メンバー関数は、返り値、関数名、引数(型とオブジェクト名)とを指定して、その中身を書くことで出来上がるものだから、メンバー関数の説明もそれに沿って書いてある
- vectorはクラスなのでメンバータイプもある。そのオブジェクトの属性のようなもので、問い合わせれば引き出せる情報のこと
- たとえばここを見ればvectorクラスの諸情報が書いてあることがわかる(1時間前までは、どう読むのかわからなかったけれど、今になってみれば、必要な情報が書いてあるし、とりあえず無視してよい部分がどこなのかもわかる)
- なるほど、コンテナというのは、値の持ち方とその取扱い方法が決まっているものだということになったので、それじゃあ、それのメンバー関数の異同がわかれば、どれを使うのがよいかもわかる。それがここに一覧表として載っている
- で、この使い勝手のよいコンテナを使うには、#include とか#include
のように、コンテナごとにincludeする
- 反復子(イテレータ)
- vectorは添え字指定でアクセスしてもよいけれど、setとかはそうはいかない
- 添え字指定でアクセスできるコンテナの場合でも、いちいち、添え字を指定するというのは、実は重い。なぜなら、順番にアクセスするにしても、そのたびに「ここ」というのを無情報から指定するから
- 代わりに、頭から終わりまで、添え字に寄らずアクセスしたりするのでよいなら、「添え字」を確認せずに「隣」にアクセスすればよい
- この「ここ」と「隣」はポインタを使えばよい
- それをするために、コンテナに反復子(イテレータ)というものを「持たせ」た実装になっている
- アルゴリズム
- 型によらずに使えるように作ってある(関数テンプレートになっている)…この辺りはHaskellの考え方と同じ
- #include とインクルードする
- 数え上げたり、ループしたり、入れ替えたり、といった「よくやること」が提供されている
- 反復子を使うものが多い。逆に言えば、添え字アクセスだと、できるコンテナとできないコンテナがあるが、反復子アクセスならすべてのコンテナに共通なので、反復子を使って実装したアルゴリズムはコンテナすべてに共用できる。そういう意味でもSTLのコンテナは反復子を持たせることで共通の扱いを実現し、その扱いを「アルゴリズム」と言って、任意コンテナ・任意型での使用を可能にしているとも言える
- アルゴリズムの使い方は、いわゆる関数のようにコード上は見える
i = count(v.vegin(),v.end(),true);
-
- これはint型のiにアルゴリズムcount()の結果を入れている
- count()はbool型ベクトルvに反復子v.begin()からv.end()までアクセスし、そのそれぞれの要素がtrueだった場合にカウントする、という作りになっている
- オブジェクトのメンバー関数的に使われていない「書きぶり」からそれと知る。
- 関数オブジェクト
- C++の基本はクラスなので、関数もクラスとして作成するし、作成されている
- 必要なものなので、重要なものがたくさんあるし、自作するときにも作りたくなる、そんなもの
- そのような関数を定義するだけのクラス単にoperator()が定義されているだけ
- STLの関数オブジェクトのincludeは#include
- 「いわゆる関数」なので、単項関数(引数が1個の関数)、二項関数(引数が2個の関数)などと分類される
- 使い方は
std::transform (numbers.begin(), numbers.end(), numbers.begin(), std::negate<int>());
-
- どう読むかというとtransform()というアルゴリズムを使ってnumbersの先頭から始めて、値のnegate(符号変更)を実施して、それをnumbers.begin()から入れていく。
- このnegate()が関数オブジェクト。型指定をして、要素ワイズに処理(符号反転)をする。transform()アルゴリズムの中で使うことでベクトル要素に全適用する