1

I am using a std::map as data holder for my algorithm but for some reason, I need to control the access of map element in some way. We know that one can access the element of the map by calling operator[key] directly. However, if the key doesn't not exist and whenever you call operator[key] it will create that key with the value initialized as 'ZERO' automatically. But in my algorithm, I will control the access by limiting that one can only modify the element when the exist key and when the value is non-zero. For example, if the map has the following elements (3, 2), (1, 0), (4, 0), (2, 7), one can only modify (3,2) and (2,7). I know that I can add some code with map::find(key) or map::count(key) anywhere before I modify the element, but it is too many so I would like to write my own container as follows

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)++;
        }
      }
   };

As you can see, instead of using map::iterator, I inherit one from std::iterator such that, the iterator refer to the MyContainer itself instead of the value type of the map. I can visit all element by

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;
}

With this idea, whenever the iterator was looped, it will return a referent the the container so I can add some code (like operator[], operator++) to control the behavior of updating the map element. For example, in this code,

void operator++(void)

will ignore any operation on non-exist key or on the element with value initialized as zero. However, there are still some doubts in the code which I am looking for your suggestions

1) if you read the code carefully, you will see that I use current_iterator to store the current map::iterator and use end_iterator to store the end iterator for the map. Those iterators will be set when MyContainer.begin() was called. The reason I need end_iterator is that if instead I set the current_iterator as map.end() then, it will change the current_iterator during the loop. For example, the following code won't work

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);
}

so when you loop the container with the following code, it won't run correctly

for (MyContainer::iterator it=h.begin(); it!=h.end(); ++it)
{
  cout << (*it).first() << " " << (*it).second() << endl;
}

that's why I write a isEnd() function in the iterator instead. But this doesn't look elegant, any better idea to get around this?

2) for the 'limited' ++ operation, if we modify the map element from the container as follows, it goes without any problem

// 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)

But if you modify that while iterating all elements, the loop will never end, why's that

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;
}

Any idea?

4

1 回答 1

1

但是如果你在迭代所有元素时修改它,循环将永远不会结束,那是什么

然后不要,而是构建要删除的对象列表,然后在循环后将其删除。

于 2012-04-19T23:03:14.797 回答