概要
自作クラスで少しのコードで拡張 for 文(イテレータ)を使える例をいくつかまとめます。
自作クラスでイテレータというとなんだか自分で1から実装しなくちゃいけない気がして憂鬱ですが、実際は以下のパターンのどれかが使える場合が案外多く、少しのコード追加だけで拡張 for 文やSTLのアルゴリズムが使えるようになるので便利です。
(なお、以下の例では全体的に std::vector
を使って書いていますが、 std::list, std::map
などでも同じですので適宜読み替えて下さい)
修正履歴
- 2017/05/15 : 「STL コンテナを public 継承した場合」セクションのコードミスを修正しました。
STL コンテナを public 継承した場合
STL コンテナを public 継承した場合には、特に何もしなくても親クラスのイテレータがそのまま使えるので拡張 for 文が使えます。ただし、 STL コンテナのデストラクタには virtual がついていないので取り扱いには注意(*)が必要です。よく継承でなくコンポジションを使うようにと推奨されています。
(*) 例えば親クラスのポインタにアップキャストして delete すると子クラスのデストラクタが呼ばれないなど。
#include <iostream>
#include <vector>
using namespace std;
using VectReal = std::vector<double>;
class MyVect : public VectReal
{
public:
MyVect(std::initializer_list<double> list)
: VectReal(list)
{ }
};
int main(void)
{
MyVect v = { 1, 2, 3, 4, 5 };
for (double d : v) { cout << d << " "; }
}
1 2 3 4 5
STL コンテナを private 継承した場合
private 継承した場合、親クラスのメンバはすべて private になるため、そのままでは拡張 for 文は使えません。ですが下記のように必要なメンバの using 宣言を追加することで親クラスのイテレータや begin, end
を使い回すことできます。
#include <iostream>
#include <vector>
using namespace std;
using VectReal = std::vector<double>;
class MyVect : private VectReal
{
public:
/* 親クラスのイテレータを使うために iterator, const_iterator, begin, end を using する */
using VectReal::iterator;
using VectReal::const_iterator;
using VectReal::begin;
using VectReal::end;
// ...その他使いたいメソッド・メンバ・typedef を using する
MyVect(std::initializer_list<double> list)
: VectReal(list)
{ }
};
int main(void)
{
MyVect v = { 1, 2, 3, 4, 5 };
for (double d : v) { cout << d << " "; }
}
1 2 3 4 5
STL コンテナをコンポジションした場合
STL コンテナをコンポジション(メンバ変数として保持)した場合も、そのままでは拡張 for 文は使えません。しかしコンポジションしたメンバ変数に対して begin, end
を委譲することでイテレータを使い回すことができます。
#include <iostream>
#include <vector>
using namespace std;
using VectReal = std::vector<double>;
class MyVect
{
public:
MyVect(std::initializer_list<double> list)
: m_vect(list)
{ }
// iterator, const_iterator の typedef をしておくと MyVect::iterator と書けて便利(必須ではない)。
using iterator = VectReal::iterator;
using const_iterator = VectReal::const_iterator;
// m_vect に処理を委譲
iterator begin() { return m_vect.begin(); }
iterator end() { return m_vect.end(); }
const_iterator begin() const { return m_vect.begin(); }
const_iterator end() const { return m_vect.end(); }
private:
// コンポジション
VectReal m_vect;
};
int main(void)
{
MyVect v = { 1, 2, 3, 4, 5 };
for (double d : v) { cout << d << " "; }
}
1 2 3 4 5
ポインタ配列をメンバに持つ場合
ポインタ配列をメンバに持つ場合にも、①ポインタをイテレータとして typedefして、② begin, end
を下記のように実装すれば簡単に拡張 for 文が使えます。 rbegin, rend
も同じ要領で実装できます。
#include <iostream>
#include <vector>
using namespace std;
class MyVect
{
public:
MyVect(std::initializer_list<double> list)
{
m_size = list.size();
m_vect = new double[m_size];
std::copy(list.begin(), list.end(), m_vect);
}
using iterator = double*;
using const_iterator = const double*;
iterator begin() { return m_vect; }
iterator end() { return m_vect + m_size; }
const_iterator begin() const { return m_vect; }
const_iterator end() const { return m_vect + m_size; }
private:
double* m_vect;
size_t m_size;
};
int main(void)
{
MyVect v = { 1, 2, 3, 4, 5 };
for (double d : v) { cout << d << " "; }
}