4

I am reading effective STL by Scott Meyers. Here in item 1 author is mentioning about how to choose among various containers and below is text snippet which I am having difficulty in understanding.

Would it be helpful to have a sequence container with random access iterators where pointers and references to the data are not invalidated as long as nothing is erased and insertions take place only at the ends of the container? This is a very special case, but if it’s your case, deque is the container of your dreams. (Interestingly,deque’s iterators may be invalidated when insertions are made only at the ends of the container. deque is the only standard STL container whose iterators may be invalidated without also invalidating its pointers and references.)

My questions on above text

  1. What does author mean by pointers and references in above context and how is it different from iterators?

  2. How deque's iterators may be invalidated when insertion made only at end and still we have valid pointers and references?

Request above two questions to be answered with simple example.

Thanks for your time and help.

venkysmarty
  • 11,099
  • 25
  • 101
  • 184
  • 1
    Inserting at the beginning or end won't change the actual memory location of the contents, so any pointers or references will continue to point to the correct object. However, if iterators are stored as an offset from the beginning for instance, an insertion will make any existing iterators have the wrong offset. However, this is a complete guess, just thinking out loud. – BoBTFish Nov 20 '12 at 13:34
  • 1
    possible duplicate of [Why does push\_back or push\_front invalidate a deque's iterators?](http://stackoverflow.com/questions/913070/why-does-push-back-or-push-front-invalidate-a-deques-iterators) – jrok Nov 20 '12 at 13:35

2 Answers2

2

For the first part, what's meant is this:

deque<int> foo(10, 1); // a deque with ten elements with value of 1
int& bar = foo.front(); // reference
int* baz = &foo.front(); // pointer
deque<int>::iterator buz = foo.begin(); // iterator
deque.push_front(0); 
// At this point bar and baz are still valid, but buz may have been invalidated

For the second part it's been covered in the detail here:

Why does push_back or push_front invalidate a deque's iterators?

Community
  • 1
  • 1
Andreas Brinck
  • 51,293
  • 14
  • 84
  • 114
1

An iterator is often used to "cycle through" the elements of a standard-library container, much like you would do with an array index, e.g. in a for loop.

Iterators can be invalid for many reasons. One common case where this happens is when you use a for loop such as the following:

std::deque<int> c;

for(std::deque<int>::iterator i = c.begin(); i != c.end(); ++i) {
    // do some stuff to the deque's elements here
}

At the end of the above loop, the iterator i will point to an "element" one block after the last real element in the deque. If you tried to do something like

*i = 88;

right after the end of the above for loop that would be a problem because the container does not "own" the memory i "points" to.

But what Meyers is likely talking about is that the Standard leaves much of the implementation of a deque open to the designer. Deques are usually implemented as linked-lists of blocks of memory holding several elements, so unlike vectors there is no guarantee that elements will be next to each other in memory. Furthermore, iterators necessarily contain information about these "blocks" so that they can traverse them smoothly (i.e. iterators are not simply pointers).

For example, if I push_back() a new element, but there is no more room in the "last" chunk of memory, then deque will need to allocate a new block of memory for the new element (and future elements added to the end). Since an iterator I was using previously might not "know" about this new chunk of memory, it could be invalid.

References and actual pointers, on the other hand, would be used in this context to refer/point to individual objects in the container. If I write

int& j = *c.begin();

then j is a reference to the first element of c. If I then do

c.push_front(74);

j still references that previous first element, even though it is no longer at the front of the deque.

However, if you insert something in the middle of the deque, then chances are you are effectively splitting one of those contiguous chunks of memory and trying to squeeze your new element in there. To make room, elements on one side or the other must be shuffled around in memory (and possibly new memory needs to be allocated). This would by necessity invalidate pointers/references to elements on that "side" of the insertion. Since it is up to the implementer how exactly room is made for an inserted element, all bets are off with respect to any pointer/reference, no matter where it is with respect to the insertion.

llakais
  • 1,577
  • 1
  • 12
  • 18
  • if we push_front we may rearrange container or allocate new junk of memory then how reference is valid even though it is no longer at the front of the queue? – venkysmarty Nov 21 '12 at 11:04
  • @venkysmarty The reference is valid in that it still refers to a valid element. That element is no longer at the front, but the reference still refers to it. A `push_front` will never rearrange the container, but it might allocate new memory. The important part is that the element that used to be at the front stays put in memory. It's just that the address of the "front" has changed. – llakais Nov 21 '12 at 17:25