Home > Software design >  Safety questions when increasing vector's capacity
Safety questions when increasing vector's capacity

Time:07-31

I've stumbled accross a case where increasing the capacity of a vector hurts one of the variables related to its element, and I would like someone to help me understanding what exactly the issue is.

Let's say, I have a class MyObject and a container vector<MyObject> myVector which was already populated with 4 elements. I also have a method:

MyObject* GetFirstActiveElement(vector<MyObject> vec)
{
    for (auto& val : vec)
    {
        if (val->IsActive())
            return &val;
    }
    return nullptr;
}

I have then a piece of code that goes as follows:

MyObject myObject new MyObject();
MyObject* firstActiveElement = GetFirstActiveElement(myVector);
myVector.insert(myVector.begin()   1, myObject); 

After the last line, if I check firstActiveElement, if it was not nullptr sometimes it is now junk.

After reading some docs, I've found that since myVector had 4 elements, and its default capacity is 4, inserting one more element causes its capacity to increase in a silent manner, whereas this C doc says:

If new_cap is greater than capacity(), all iterators, including the past-the-end iterator, and all references to the elements are invalidated. Otherwise, no iterators or references are invalidated.

I actually thought that firstActiveElement is just a pointer, so it should not be invalidated in any case. But apparently, it happens to be an interator or a reference to a vector, is that true? I'm a bit lost here, but I guess the reason is my design of the method GetFirstActiveElement().

CodePudding user response:

Any access to the value returned by GetFirstActiveElement is always undefined behaviour, since the vector is passed by value to the function, inside the function you're dealing with copies of the MyObjects stored in the vector inside the calling function; those copies get destroyed when returning.

Even if you pass a reference resizing the vector may result in the addresses of the vector elements changing (or rather different objects being constructed in the new backing storage by moving the old objects.

The following example demonstrates this:

int main() {
    std::vector<int> v;
    v.push_back(1);
    void* p1 = &v[0];
    v.reserve(1000);
    void* p2 = &v[0];

    std::cout << "p1=" << p1 << "\np2=" << p2 << '\n';
}

Possible output:

p1=000001B4B85C5F70
p2=000001B4B85D29B0

If you want to keep addresses of the MyObjects stable, you could use a std::vector<std::unique_ptr<MyObject>> which however means that the vector can only be moved, not copied.

  • Related