2

In the code below, the Struct1 pointer in Struct2 should point consistently to a certain object of Struct1. Each of these structs is contained in a vector.

However, when I output the index variable of the Struct1 objects pointed to in the Struct2 objects, in some cases the wrong one is returned.

Why does a pointer to an object contained in a vector sometimes point to a different object in the vector?

struct Struct1
{
    size_t index;
    std::vector<size_t> data;
}

struct Struct2
{
    Struct1 *s1;
}

class MyClass
{
    std::vector<Struct1> s1s;
    std::vector<Struct2> s2s;
    size_t propIndex = 0;

    void input(QByteArray &line)
    {
        if (line == "1") {
            for (size_t i = s1s.size(); i <= propIndex; i++) {
                s1s.push_back({ .index = i, .data= {} });
            }
            QByteArrayList list = getList();
            for (auto id : list)  s1s.at(propIndex).data.push_back(id.toULongLong());
        }
        else {
            if (propIndex == s2s.size()) {
                s2s.push_back({ .s1 = nullptr });
            }

            if (line == "2") {
                size_t index = getIndex();
                for (size_t i = s1s.size(); i <= index; i++) {
                    s1s.push_back({ .index = i, .data= {} });
                }
                s2s.at(propIndex).s1 = &s1s.at(index);
            }
        }

        propIndex++;
    }

    QByteArrayList output()
    {
        QByteArrayList output;

        for (auto s2 : s2s) {
            output += QByteArray::number(s2.s1->index) + "\n";
        }

        return output;
    }
}
2
  • Hello! Please read Why is "Can someone help me" not an actual question. To make this question answerable, please elaborate on the expected and actual behavior of your code. Commented Mar 8, 2020 at 20:15
  • @Brian I've tried to rephrase the question :) Commented Mar 8, 2020 at 20:29

2 Answers 2

2

The problem is that you take a pointer to an item in a vector:

s2s.at(propIndex).s1 = &s1s.at(index);

The vector is a dynamic structure and its data may be reallocated when it grows. So any push_back() could invalidate all the pointers:

s1s.push_back({ .index = i, .data= {} });

Note that the vector allocation algorithm is designed to reserve space for several elements when it needs to grow. This explains that the issue appears only from time to time.

One solution could be to keep not a pointer but the index of the elements together with the a pointer to the vector.

Sign up to request clarification or add additional context in comments.

6 Comments

Thanks, that explains it. I think the solution you propose would kind of invalidate why I'm using the pointers in the first place, but in any case I'll have to find a different approach.
@xaxxon yes, I was thinking of proposing shared_ptr (because by design these vectors would be shared between s1s and s2s). But I think that OP wanted this vector for allocating the objects in the first place.
So what I could do is allocate the objects independently and put unique_ptrs to them in the vector instead? And if so, the objects would be destroyed once its unique_ptr is released/destroyed, right?
@bur yes, this is indeed an idea. but unique_ptr are meant to stay unique. You can use this if your s2s is the only one to use it. If you need several users of the same pointer (e.g. in s2s and s1s), go for a shared_ptr instead.
@Christophe only used a shared ptr if you want shared ownership. Otherwise a vector of unique ptr and a vector of raw pointers is what you want. In his case, ownership lifetimes are identical, so no need for the extra overhead of a shared ptr.
|
1

As discussed in the comments on the accepted answer, a possible solution is using unique_ptrs.

In the OPs example code, the Struct1 vector would be replaced by a unique_ptr vector:

    std::vector<std::unique_ptr<Struct1>> s1s;

Then the objects need to be allocated independently and a unique pointer needs to be created to store in the vector, and we need to call .get() to get a raw pointer to the object:

    if (line == "1") {
        for (size_t i = s1s.size(); i <= propIndex; i++) {
            s1s.push_back(std::unique_ptr<Struct1>(new Struct1({ .index = i, .data = {} })));
        }
        QByteArrayList list = getList();
        for (auto id : list)  s1s.at(propIndex)->data.push_back(id.toULongLong());
    }

...

            if (line == "2") {
            size_t index = getIndex();
            for (size_t i = s1s.size(); i <= index; i++) {
                s1s.push_back(std::unique_ptr<Struct1>(new Struct1({ .index = i, .data = {} })));
            }
            s2s.at(propIndex).s1 = s1s.at(index).get();

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.