[ 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コンテナのようにアルゴリズムから扱えると便利です。