アルゴリズムのデータホルダーとしてstd::mapを使用していますが、何らかの理由で、何らかの方法でmap要素のアクセスを制御する必要があります。operator [key]を直接呼び出すことで、マップの要素にアクセスできることがわかっています。ただし、キーが存在しない場合、operator [key]を呼び出すと、値が「ZERO」として自動的に初期化されたキーが作成されます。しかし、私のアルゴリズムでは、存在するキーと値がゼロ以外の場合にのみ要素を変更できるように制限することで、アクセスを制御します。たとえば、マップに次の要素(3、2)、(1、0)、(4、0)、(2、7)がある場合、変更できるのは(3,2)と(2,7)のみです。要素を変更する前に、map :: find(key)またはmap :: count(key)を使用してコードを追加できることは知っていますが、多すぎるため、次のように独自のコンテナーを作成します。
class MyContainer;
template <typename T> class myiterator :public iterator<forward_iterator_tag, T>
{
friend class MyContainer;
private:
T *pointer;
myiterator(T *pointer):pointer(pointer) {}
public:
T& operator*() {return (*pointer);}
const myiterator<T>& operator++()
{
pointer->current_iterator++;
return *this;
}
bool operator!=(const myiterator<T>& other) const
{
return pointer->current_iterator != other.pointer->current_iterator;
}
bool isEnd(void) const
{
return pointer->current_iterator == pointer->end_iterator;
}
};
class MyContainer
{
friend class myiterator<MyContainer>;
public:
typedef myiterator<MyContainer> iterator;
private:
map<int, int> data;
map<int, int>::iterator current_iterator, end_iterator;
public:
MyContainer() {current_iterator = data.begin(); }
void addDataPair(int key, int value) {data[key] = value;}
int first() {return (*current_iterator).first;}
int second() {return (*current_iterator).second;}
// initialize the current_iterator to the begin of the data (map) and set the end iterator too
iterator begin()
{
current_iterator = data.begin();
end_iterator = data.end();
return myiterator<MyContainer>(this);
}
// return the container w/ current_iterator point to where the key is
MyContainer &operator[](int key)
{
current_iterator = data.find(key);
return (*this);
}
// only increase the value by one when the key does exist and with initial value non-zero
void operator++(void)
{
if ( (current_iterator != data.end()) &&
((*current_iterator).second>0) )
{
((*current_iterator).second)++;
}
}
};
ご覧のとおり、map :: iteratorを使用する代わりに、std :: iteratorから継承し、イテレーターがマップの値型ではなくMyContainer自体を参照するようにします。私はすべての要素を訪問することができます
MyContainer h;
h.addDataPair(1, 3);
h.addDataPair(2, 4);
h.addDataPair(3, 0);
h.addDataPair(7, 9);
h.addDataPair(11, 2);
for (MyContainer::iterator it=h.begin(); !it.isEnd(); ++it)
{
cout << (*it).first() << " " << (*it).second() << endl;
}
このアイデアでは、イテレーターがループされるたびに、コンテナーの指示対象が返されるため、マップ要素の更新動作を制御するコード(operator []、operator ++など)を追加できます。たとえば、このコードでは、
void operator++(void)
存在しないキーまたは値がゼロとして初期化された要素に対する操作はすべて無視されます。しかし、私があなたの提案を探しているコードにはまだいくつかの疑問があります
1)コードを注意深く読むと、current_iteratorを使用して現在のmap :: iteratorを格納し、end_iteratorを使用してマップの終了イテレータを格納していることがわかります。これらのイテレータは、MyContainer.begin()が呼び出されたときに設定されます。end_iteratorが必要な理由は、代わりにcurrent_iteratorをmap.end()として設定すると、ループ中にcurrent_iteratorが変更されるためです。たとえば、次のコードは機能しません
iterator begin()
{
current_iterator = data.begin();
return myiterator<MyContainer>(this);
}
iterator end()
{
current_iterator = data.end(); // here we set current_iterator as data.end(), but this will change the current iterator of data too
return myiterator<MyContainer>(this);
}
したがって、次のコードでコンテナをループすると、正しく実行されません
for (MyContainer::iterator it=h.begin(); it!=h.end(); ++it)
{
cout << (*it).first() << " " << (*it).second() << endl;
}
そのため、代わりにイテレータでisEnd()関数を記述します。しかし、これはエレガントに見えません。これを回避するためのより良いアイデアはありますか?
2)「制限付き」++操作の場合、コンテナからマップ要素を次のように変更すると、問題なく実行されます。
// assuming the map initially contains (2, 4), (3, 0), (7, 9), (11, 2)
h[4]++; // modify the element with key==4, won't do anything, no such key
h[3]++; // modify the element with key==3, won't do anything, value=0
h[11]++; // modify the element with key==11, then we have (11, 3)
(*(h.begin()))++; // modify the first element, works, we have (2,5)
しかし、すべての要素を繰り返しながらそれを変更すると、ループは決して終了しません。それはなぜですか
for (MyContainer::iterator it=h.begin(); !it.isEnd(); ++it)
{
(*it)++; // it works
(*it)[3]++; // it will cause the loop run and never stop
cout << (*it).first() << " " << (*it).second() << endl;
}
何か案が?