views:

131

answers:

6

In a program to simulate logic gates I switched from using arrays

node N[1000];

to vectors

vector<node> N;

And my program did work perfectly before using vectors but now it prints wrong results, so I tried debugging and I found out that the bug happens here:

node* Simulator::FindNode(string h)
{
    int i;
    for(i = 0; i < NNodes; i++)
    {
        if (N[i].getname() == h)
        {
            return &N[i];
        }
    }

    node n ;
    N.push_back(n);
    N[NNodes].setname(h);
    NNodes++;
    return &N[NNodes-1]; //why?because of NNodes++  
}

// ...

node* inp1;
node* inp2;
node* out;
string NodeName;

inp_file >> NodeName;
inp1 = FindNode(NodeName);
s1 = inp1;

inp_file >> NodeName;
inp2 = FindNode(NodeName); //inp1 is destroyed here 

inp_file >> NodeName;
out = FindNode(NodeName); //inp2 and inp1 are destroyed here 

When calling FindNode for the 1st time, the 1st pointer inp1 points to the right place which is &N[0].

When calling FindNode for the second time the 1st pointer inp1 points to rubbish and the second pointer inp2 points to the right place &N[1].

When calling FindNode for the 3rd time the both the 1st and 2nd pointers (inp1, inp2) point to rubbish! And 3rd pointer out points to the right place.

Why would that happen?
How does vector work when I insert items to them and which kind of pointers should I use to point to vectors items?

+8  A: 

Yes, it can re-allocate the entire buffer, making all pointers into the old location invalid.

You can limit this by preallocating, but that's really just a performance boost. The better way is to use indexes instead of raw pointers.

Steven Sudit
So , how should i point to those elements ?
Ahmed
Replace `node*` with `int`, and store the index. When you need a node, you'll have to look it up in the vector, using that index.
Steven Sudit
I don't clearly get that .can you explain on a code example ?
Ahmed
If you must keep the pointer semantics, you can use std::list instead: http://www.sgi.com/tech/stl/List.html Instead of pointers you use iterators into this list (std::list<node>::iterator). Iterators into this list behave like pointers, and they keep their value when elements are inserted or removed. std::list consumes more memory, and there is no random access "N[i]".
Eike
That's interesting: I appear to have been downvoted for no reason whatsoever. Gotta love SO.
Steven Sudit
In any case, I think GMan's dictionary suggestion may well be a better fit.
Steven Sudit
+4  A: 

When a vector grows, it is reallocated, which effectively invalidates all pointers to elements of the vector.

If you know beforehand how many elements you will have in the vector, you could use the reserve() method to preallocate space.

PeterK
I don't , It depends on user input .
Ahmed
Peter, I mentioned preallocation, but I wouldn't recommend it as a solution for pointers being invalidated, only as a performance optimization. What's the point of using a `List<>` if you have to size it in advance? Might as well just use a native array, then.
Steven Sudit
I know, but nevertheless a vector offers more benefits than just a "better array". Anyway i agree with you, your solution is better.
PeterK
Peter, you're not wrong. If we knew that there was some reasonable maximum, we could reserve the full size while still having the semantics of an expandable vector with a valid current length. The only problem is that the OP says there is no such max.
Steven Sudit
A: 

Returning a pointer to an internal STL member is probably not the best idea. When you give an object to an STL container you are basically giving up control of it. You are telling the STL it can move it around as it sees fit to maintain the promises that the container gives you. Returning the index where the node is located is a better idea like Steven Sudit mentioned.

Once you get the index you could create a function that returns a copy of the contents of the node you are interested in. This way you also maintain data encapsulation with the STL container, not allowing anyone else to modify its contents.

MadcapLaugher
Sure, but it would be fine if the pointers were taken after they stopped changing the container: STL guarantees that this is safe.
Steven Sudit
And, as a few others have pointed out, the containers that don't use large contiguous blocks *do* guarantee that the address will not change.
Steven Sudit
A: 

Yes, inserts will invalidate old pointers to vector elements on reallocation. If you want to use stable pointers really hard, you can switch from vector to deque. It offers a very similar interface to vector and can grow without reallocating and moves previous contents by allocating further chunks.

The price you pay for using a deque instead of a vector is one more level of indirection on random access. Depending on your usage, that may be totally irrelevant. You should iterate over deque the whole deque if necessary by using iterators. That will be as fast a iterating over a vector.

The gain is: zero reallocations!

Peter G.
A: 

Imagine you needed to write the array-based code so that it could possibly re-size the array if necessary.

Imagine how you would do it.

Imagine what would happen to stale pointers.

Rewrite your code to use indices rather than pointers, or to guarantee that re-allocation doesn't happen, as appropriate.

Jon Hanna
+4  A: 
GMan
Lots of good advice here and very comprehensive. Thanks.
Steven Sudit
So many tips , Thank You !
Ahmed
After some trials I concluded that the first route , which was using some indirection won't work well If you need to use pointers in the program . Many problems appeared and there must be unnecessary complexity to solve them , So I used a deque and it worked like charm .
Ahmed
@Ahmed: Good. :)
GMan