ぐぐるからのお客さんの傾向を調べてたら頻出キーワードに「STL」があると判明。なのでSTLに関して何か書いてみようとおもいます。
STLだけでは広すぎるので、STLと一緒に検索されたキーワードで最も多かった「スマートポインタ」を絡めてみます。
さて、STLで検索してきてる人は、多分STLがなんであるかはご存知のはずですね。
STLは標準C++に付属する標準ライブラリの一部で、テンプレートを用いたコンテナやアルゴリズムなんかのライブラリを指します。
入門書の類などで特によく使われるのstringやvector、iostreamなんかもSTLに含まれるジェネリッククラスですね。
さて、このSTLにはコンテナ、アルゴリズム、ユーティリティ、イテレータなどが含まれます。
コンテナはデータ構造、アルゴリズムは言葉の通り、ユーティリティはちょっとした処理をテンプレート化した関数群、イテレータはデータ構造体を巡回する際などに使うポインタの代わりになるものです。
最後に挙げたイテレータ、これってある意味スマートポインタなんですよね。
スマートポインタと言えるほどの汎用性は無いんですが、STLコンテナの中を巡回することに関してはまさに「賢いポインタ」として動作してくれます。
まずC言語での動的配列のイディオム。
int *ida = (int*)malloc(sizeof(int) * 10);
があった時、ポインタとポインタ演算を用いてこの配列の中身を頭から順番に設定していくコードは以下のようになります。
int *ip = ida;
int i;
for(i = 0; i < 10; ++ip, ++i){
/* something() はint型の何らかの値を返する関数 */
*ip = something();
}
これを、C++での動的配列のイディオムで表現すると。
std::vector<int> ida(10);
for(std::vector<int>::iterator ip = ida.begin();
ip != ida.end();
++ip){
// something() はint型の何らかの値を返す関数
*ip = something();
}
ipに対する操作に着目するととても似てますね。イテレータはポインタを模して作られているのです。
しかしこれだけだとありがたみがわかりませんね。そこでリンクリストに関しても見てみましょう。
C言語だと、典型的なリンクリストの巡回は以下のようになります。
typedef struct list_tag{
int value_;
struct list_tag *next_;
} List;
/* (snip) */
List *iter;
for(iter = Data; iter != DataEnd; iter = iter->next_){
/* something() はint型の引数を受け取る関数 */
something(iter->value_);
}
C++で書いてみましょう。
std::list<int> Data;
// (snip)
for(std::list<int>::iterator iter = Data.begin()
iter != Data.end();
++iter){
// something() はint型の引数を受け取る関数
something(*iter);
}
iterに着目したとき、C言語版よりも楽になった感じです。なにより、C++版だと、iterに対する操作は本質的にvectorの場合と変わってません。
ただのポインタの場合、データ構造ごとに違う探索操作をしなければならないところを、実際の動作を隠蔽して統一的な操作で行ってしまうのがイテレータの魅力です。ことコンテナの探索に関しては立派にスマートポインタと言えます。
しかし、検索で来てる方々は、こういったものではなくて汎用のスマートポインタを探してますよね。
STLにも一つ、汎用のスマートポインタがあります。auto_ptrがそれです。もちろんデストラクト時にちゃんとdeleteしてくれます。
auto_ptrは破壊的スマートポインタと呼ばれる方法で実装されたスマートポインタです。あるポインタについて、一つだけ所有権を認めます。
例えば以下のようにすると、aは0 (NULL) を指し、aの指していた領域をbが指すようになります。
auto_ptr<int> a(new int(10));
auto_ptr<int> b(a);
あるいは
auto_ptr<int> a(new int(10));
auto_ptr<int> b;
b = a;
ソース側の状態を破壊するので破壊型という訳です。しかしこれなら確かにリークはありません。
しかしこの方法、割と問題あるんですよね。例えば。
// 参照渡しのつもりでポインタ型引数を受け取って二倍にして引数経由で値を返す関数
void Double(int* ptr){ *ptr *= 2; }
// 生のポインタは怖いからスマートポインタ用にオーバーロード
void Double(auto_ptr<int> ptr){ *ptr *= 2; }
int main(){
int i = 20;
auto_ptr<int> ptr(new int(10));
Double(&i);
Double(ptr);
cout << i << ", " << *ptr << endl; // 不正な参照はがし
return 0;
}
Double(int*)はうまく動きますが、Double(auto_ptr<int>)はうまく動きません。
Double(auto_ptr<int>)では、auto_ptr<int>を値渡ししています。なので、実引数はコピーされるのです。auto_ptrではコピーが発生すると、ソース側が破壊されるので、関数本体に入った時点で既に呼び出し側のptrには0が設定されています。なので出力行での参照はがしは不正なメモリアクセスとなります。なので上記のような場合は参照で渡しましょう。
また、ポインタを保持するコンテナではポインタの代わりにauto_ptrを使おうとすると痛い目に遭うかもしれません。
vector<auto_ptr<int> > iapv1;
vector<auto_ptr<int> > iapv2;
iapv1.push_back(new int(10));
iapv1.push_back(new int(20));
iapv2 = iapv1; // ここでコピーが発生。iapv1の保持するポインタが破壊される
*iapv1[0] = 100; // 既にiapv1[0]はヌルポインタなのでエラー
正直知らないと気付きません(´・ω・`)
破壊型という特性故にauto_ptrは使いづらいと言う評価を受けるようです。長月もスマートポインタをご所望の方はBoostのshared_ptrを使ったほうが良いかと思います。
それでは今回はここまで。また気が向けばやってみます。当日記に出てきたキーワードで、何か知りたいことがある方はぐぐる経由でアクセスしまくるといいかもですよ? であであノシ
#ちなみに最頻出クエリは「hidew」でした(絶爆) おめでたうw>hidewさん
らるちゃんも大好き村山由香を読みましょう。
「おいしいコーヒーの入れ方」はゲロ甘で某おねぇたまにはマジお勧めできない
それマンガ? 小説? お値段は? 何巻でてる?
小説だな。
集英社で赤色の大きい本だけど、今は文庫本サイズもでてるはず。
おいコーシリーズは今のところ6巻ぐらいでてるはず。
短編も結構な数がでててわかんね。
中間文学と呼ばれる分野では第一人者かな。
ほむ、文庫版見つけたら買ってみるか。
情報さんくしヽ(´ワ`)ノ
代償行為なんざいいから、MH書きましょうよー
マジで待ってるんですよ~?
それはもうこの際しーたんに書いてもらいませう(´∀`)
てことでまかせた>しんご