[ C++で開発 ]
STLを使いこなすためのノートです。STLは機能豊富で汎用的なライブラリ集ですが、使いこなすには難易度が少々高いので、あらかじめ勉強が欠かせません
オブジェクト指向プログラマーがぶつかるSTLの壁の1つです。
オブジェクト指向プログラミングをする以上、アプリケーションプログラムを書く際にはクラスをいくつも定義します。ポリモーフィズムを活用するため継承を使います。しかし、STLのコンテナにオブジェクトを入れようとして、はたと悩みます。世の中のSTLのサンプルは、コンテナに値をコピーで格納します。コピーだと、ポリモーフィズムが使えません。困った・・・
そこで、苦肉の策としてコンテナにポインタを格納することにします。
#include <list> class Person { ... }; class Employee : public Person { ... }; void testPointer() { std::list<Person*> persons; persons.push_back(new Person("Thomas")); persons.push_back(new Person("Percy")); persons.push_back(new Employee("Henry")); for_each(persons.begin(), persons.end(), std::mem_fun(&Person::print)); } |
オブジェクト指向プログラミングでは、クラスのメンバー関数にそのクラスのオブジェクトに対する操作を定義するので、STLを使用する場合、アルゴリズムの関数オブジェクトとしてクラスのメンバー関数を指定したくなります。
ポインタを要素にしたコンテナの場合、各要素はポインタなので、std::mem_funでメンバー関数のアドレスを指定します。
これを実行すると、以下の実行イメージになります。
Person{name=Thomas} Person{name=Percy} Employee{name=Henry}
さて、これで終われば簡単なのですが、上記コードはメモリ管理の問題があります。関数testPointerのスコープを抜けると、ローカル変数として定義したpersonsは破棄されますが、その要素の各ポインタが指すメモリは破棄されません。
... persons.clear(); } |
std::listには、全要素を除去するclear()がありますが、残念ながらポインタをdeleteはしてくれません。
... for (std::list<Person*>::iterator it = persons.begin(); it != persons.end(); ++it) { delete *it; } persons.clear(); } |
for文でiteratorを操作してもいいのですが、ここではSTLアルゴリズムっぽくfor_eachを使ってみます。
struct DeleteObject { template <typename T> void operator()(const T* ptr) const { delete ptr; } }; ... for_each(persons.begin(), persons.end(), DeleteObject()); persons.clear(); } |
ポインタはメモリ管理が難しいので、スマートポインタを使う方法が適しているでしょう。
#include <list> #include <boost/shared_ptr.hpp> #include <boost/mem_fn.hpp> class Person { ... }; class Employee : public Person { ... }; typedef boost::shared_ptr<Person> PersonPtr; void testSmartPointer() { std::list<PersonPtr> persons; persons.push_back(PersonPtr(new Person("Thomas"))); persons.push_back(PersonPtr(new Person("Percy"))); persons.push_back(PersonPtr(new Employee("Henry"))); for_each(persons.begin(), persons.end(), boost::mem_fn(&Person::print)); } |
関数testSmartPointerのスコープを抜けてstd::listが破棄されるときに、各要素のスマートポインタで管理されるポインタがdeleteされます。
スマートポインタには、std::mem_funとstd::mem_fun_refは適用できないため、boost::mem_fnを使用しています。
配列をSTLコンテナのようにアルゴリズムから扱えると便利です。