1

I have spent a lot of time to search reason of errors. Below is a piece of my c++ program which is using vector to store data. I have seen that when vector increase self size, then all references are change. So save references to push_back() object by back() object is stupid and does not make sense.

class Test{
    public :
        int wiek;
};

int main(int argc, char **argv)
{
    std::vector<Test> v;
    std::vector<Test*> vv;

    for (int i = 0; i < 12; i++) {
        Test t;
        t.wiek = 10;
        v.push_back(t);
        vv.push_back(&v.back());
    }

    Test& m = v.back();

    for (int i = 0; i < 24; i++) {
        Test t;
        t.wiek = 123;
        v.push_back(t);
        vv.push_back(&v.back());
    }

    for (int i = 0; i < 36; i++) {

        std::cout<<"vv.at(i)->wiek : "<<vv.at(i)->wiek<<"\n";
    }

    // vv.at(i)->wiek : 1619638984
    // vv.at(i)->wiek : 167838876
    // vv.at(i)->wiek : 167838896
    // vv.at(i)->wiek : 167838900
    // vv.at(i)->wiek : 167839152
    // vv.at(i)->wiek : 167839156
    // vv.at(i)->wiek : 167839160
    // vv.at(i)->wiek : 167839164
    // vv.at(i)->wiek : 167838872
    // vv.at(i)->wiek : 167838876
    // vv.at(i)->wiek : 167838880
    // vv.at(i)->wiek : 167838884
    // vv.at(i)->wiek : 167838888
    // vv.at(i)->wiek : 167838892
    // vv.at(i)->wiek : 167838896
    // vv.at(i)->wiek : 167838900
    // vv.at(i)->wiek : 123
    // vv.at(i)->wiek : 123
    // vv.at(i)->wiek : 123
    // vv.at(i)->wiek : 123
    // vv.at(i)->wiek : 123
    // vv.at(i)->wiek : 123
    // vv.at(i)->wiek : 123
    // vv.at(i)->wiek : 123
    // vv.at(i)->wiek : 123
    // vv.at(i)->wiek : 123
    // ...
    //
    return 1;
}

Do I have right?

Rama
  • 3,222
  • 2
  • 11
  • 26
ttmdear
  • 23
  • 5
  • 2
    Do you have what right? Where is your confusion? You say you already know what you are doing is invalidating the references. – NathanOliver Apr 05 '17 at 16:40
  • 1
    @Eddge not exactly. They are pushing a local onto the vector (makes a copy), then saving the address of the position of the added vector element. Similar but different from directly storing the address of an automatic storage variable. – crashmstr Apr 05 '17 at 16:43
  • @crashmstr you're right thanks for the correction. – AresCaelum Apr 05 '17 at 16:45
  • 1
    Yes this is not a good thing to do. never use references or pointers to members after changing its allocated size. – Galik Apr 05 '17 at 16:46
  • I believe the issue then is when a vector resize's it does not guarantee the data that was previous store will be at the same memory address, so us adding to the vector and storing the address of the back in a second vector, there is no guarantee that data after a resize will be at the same memory address. – AresCaelum Apr 05 '17 at 16:47
  • 3
    good read: http://stackoverflow.com/questions/6438086/iterator-invalidation-rules – NathanOliver Apr 05 '17 at 16:48
  • 1
    Read about the vector invalidation rules here: http://en.cppreference.com/w/cpp/container/vector#Iterator_invalidation – Jesper Juhl Apr 05 '17 at 16:51
  • @NathanOliver: Thanks :) – Lightness Races in Orbit Apr 05 '17 at 18:09

2 Answers2

1

If you need a container which permits random access and which guarantees that item references are not invalidated when the container grows, use std::deque.

rici
  • 234,347
  • 28
  • 237
  • 341
0

Correct, what you're doing with your vector doesn't exactly make sense. When you call push_back() and the vector's size changes; there's an automatic reallocation of the storage for the entire vector. So potentially, the previously added elements may no longer exist at their original address.

A quick fix for this is to pre-allocate all of the indexes you'd wish to use using v.resize(). So the following code should work:

#include <vector>
#include <iostream>

class Test{
    public :
        int wiek;
};

int main(int argc, char **argv)
{
    std::vector<Test> v;
    std::vector<Test*> vv;

    v.resize(36);

    for (int i = 0; i < 12; i++) {
        Test t;
        t.wiek = 10;
        v.push_back(t);
        vv.push_back(&v.back());
    }

    Test& m = v.back();

    for (int i = 0; i < 24; i++) {
        Test t;
        t.wiek = 123;
        v.push_back(t);
        vv.push_back(&v.back());
    }

    for (int i = 0; i < 36; i++) {

        std::cout<<"vv.at(i)->wiek : "<<vv.at(i)->wiek<<"\n";
    }

//    vv.at(i)->wiek : 10
//    vv.at(i)->wiek : 10
//    vv.at(i)->wiek : 10
//    vv.at(i)->wiek : 10
//    vv.at(i)->wiek : 10
//    vv.at(i)->wiek : 10
//    vv.at(i)->wiek : 10
//    vv.at(i)->wiek : 10
//    vv.at(i)->wiek : 10
//    vv.at(i)->wiek : 10
//    vv.at(i)->wiek : 10
//    vv.at(i)->wiek : 10
//    vv.at(i)->wiek : 123
//    vv.at(i)->wiek : 123
//    vv.at(i)->wiek : 123
//    vv.at(i)->wiek : 123
//    vv.at(i)->wiek : 123
//    vv.at(i)->wiek : 123
//    vv.at(i)->wiek : 123
//    vv.at(i)->wiek : 123
//    vv.at(i)->wiek : 123
//    vv.at(i)->wiek : 123
//    vv.at(i)->wiek : 123
//    vv.at(i)->wiek : 123
//    vv.at(i)->wiek : 123
//    vv.at(i)->wiek : 123
//    vv.at(i)->wiek : 123
//    vv.at(i)->wiek : 123
//    vv.at(i)->wiek : 123
//    vv.at(i)->wiek : 123
//    vv.at(i)->wiek : 123
//    vv.at(i)->wiek : 123
//    vv.at(i)->wiek : 123
//    vv.at(i)->wiek : 123
//    vv.at(i)->wiek : 123
//    vv.at(i)->wiek : 123

    return 0;
}

However, If you don't know what the final size would be. You'd have to create the entirety of the "v" vector prior to assigning all of its element's addresses to "vv".

Darkblue
  • 16
  • 3
  • 1
    You can use `std::list` instead. http://en.cppreference.com/w/cpp/container/list "Addition, removal and moving the elements within the list or across several lists does not invalidate the iterators or references. " – Ceros Apr 05 '17 at 17:14
  • @Ceros True, changing the container type would also work. However, I was under the impression that the question was about the behavior of vector, not necessarily to solve the source code problem? It's a rather strangely worded original question. – Darkblue Apr 05 '17 at 17:28