一般的な目標
オブジェクトのコレクションを管理します(簡単Collection
なReal
例として)。次に、コレクションに反復子を定義しました。つまりiterator
、 、const_iterator
、reverse_iterator
およびconst_reverse_iterator
. iterator
この例では、とだけに注目しconst_iterator
ます。他の 2 つは非常によく似ています。
その後、特定の条件に関して要素を保持するかどうかを指定するコレクションのフィルターを定義したいと思います。Real
例として、正の値を持つインスタンスのみを保持します。また、保持された要素のみでコレクションを反復処理したいと思います。
コレクションの実装方法
この例では、コレクション内の私のオブジェクトは非常に単純です。目標は、ネイティブ型の代わりにオブジェクトを持つことです:
struct Real
{
public:
double r;
};
次に、内部の実際のコンテナーを知らなくても、コレクションを定義します。
class Collection
{
public:
typedef std::vector<Real>::iterator iterator;
typedef std::vector<Real>::const_iterator const_iterator;
private:
std::vector<Real> data;
public:
Collection() : data() {}
Collection(unsigned long int n) : data(n) {}
Collection(unsigned long int n, const Real& x) : data(n,x) {}
Collection::iterator begin() { return this->data.begin(); }
Collection::iterator end() { return this->data.end(); }
Collection::const_iterator begin() const { return this->data.begin(); }
Collection::const_iterator end() const { return this->data.end(); }
};
これは、次の単純な例で非常にうまく機能しています。
int main()
{
Collection c(5);
double k = 1.0;
for(Collection::iterator it = c.begin(); it != c.end(); ++it)
{
it->r = k;
k *= -2.0;
}
std::cout << "print c with Collection::iterator" << std::endl;
for(Collection::iterator it = c.begin(); it != c.end(); ++it)
std::cout << it->r << std::endl;
std::cout << "print c with Collection::const_iterator" << std::endl;
for(Collection::const_iterator it = c.begin(); it != c.end(); ++it)
std::cout << it->r << std::endl;
return 0;
}
そして、このプログラムは期待される出力を書き込みます:
print with Collection::iterator
1
-2
4
-8
16
print with Collection::const_iterator
1
-2
4
-8
16
フィルターの実装方法
ここで、コレクションへの参照またはポインターを持ち、反復子を持ち、フィルターを介して値を受け入れる抽象関数を持つ抽象フィルターを作成したいと考えています。この最初のステップでは、イテレータを使用せずにクラスのみを作成しました。
class CollectionFilter
{
private:
Collection& col;
public:
CollectionFilter(Collection& c) : col(c) {}
virtual ~CollectionFilter() {}
Collection& collection() { return this->col; }
iterator begin() { /* todo */ }
iterator end() { /* todo */ }
const_iterator begin() const { /* todo */ }
const_iterator end() const { /* todo */ }
virtual bool accept(const Real& x) const = 0;
};
次に、特定の条件を実装する新しいフィルターを作成するのは非常に簡単です。
class CollectionFilterPositive : public CollectionFilter
{
public:
CollectionFilterPositive(Collection& c) : CollectionFilter(c) {}
virtual ~CollectionFilterPositive() {}
virtual bool accept(const Real& x) const { return x.r >= 0.0; }
};
フィルターに反復子を実装する前に、いくつかのコメント/質問があります。
- このフィルターは非 const
Collection&
で機能しますが、begin() const
andend() const
関数は本当に必要ですか? はいの場合、なぜですか? - にフィルターを適用することはできませんが、
const Collection&
私の目標には明らかに必要です。それを行う良い方法は何ですか?CollectionFilter
クラスCollectionFilterConst
を非常によく似たコードを持つクラスに複製する必要がありますか? さらに、このソリューションは、2 つの類似したクラスから継承する必要があるユーザーにとって非常に混乱します。
それでは、イテレータの実装に行きましょう。この例では、 のみを記述し、 は記述しiterator
ませんでしたconst_iterator
。これをクラスに追加します:
class CollectionFilter
{
public:
class iterator
{
private:
CollectionFilter* filter;
Collection::iterator iter;
public:
iterator(CollectionFilter* f, Collection::iterator i) : filter(f), iter(i) {}
iterator(const iterator& i) : filter(i.filter), iter(i.iter) {}
iterator& operator = (const iterator& i) { this->filter = i.filter; this->iter = i.iter; return *this; }
iterator& operator ++ ()
{
if(this->iter != this->filter->collection().end())
{
do
{
++this->iter;
} while(this->iter != this->filter->collection().end() && !this->filter->accept(*this->iter));
}
}
iterator operator ++ (int) { /* similar */ }
Real& operator * () { return *this->iter; }
Collection::iterator operator -> () { return this->iter; }
bool operator == (const iterator& i) const { return this->iter == i.iter; }
bool operator != (const iterator& i) const { return this->iter != i.iter; }
};
public:
iterator begin()
{
Collection::iterator it = this->col.begin();
if(!this->accept(*it)) ++it;
return CollectionFilter::iterator(this,it);
}
iterator end()
{
Collection::iterator it = this->col.end();
return CollectionFilter::iterator(this,it);
}
};
これは、この単純な例でもうまく機能しています
int main()
{
Collection c(5);
double k = 1.0;
for(Collection::iterator it = c.begin(); it != c.end(); ++it)
{
it->r = k;
k *= -2.0;
}
std::cout << "print c with CollectionFilterPositive::iterator" << std::endl;
CollectionFilterPositive fc(c);
for(CollectionFilterPositive::iterator it = fc.begin(); it != fc.end(); ++it)
std::cout << it->r << std::endl;
return 0;
}
期待される出力を与える:
print with CollectionFilterPositive::iterator
1
4
16
繰り返しますが、いくつかの質問:
- 私はこのアプローチで完全に間違っていますか?
- わずかな変更だけで
CollectionFilter::iterator
実装するには、 のコードを複製する必要があると思います。CollectionFilter::const_iterator
このコードの重複を避ける方法はありますか (重複したクラスCollectionFilterConst
と逆の反復子を数えると、8 回書かれています)。 - コードの const の正確さに満足できません。いくつか問題がありますか?
前もって感謝します !