Home > Blockchain >  Why std::vector::resize works like this with matrix of objects
Why std::vector::resize works like this with matrix of objects

Time:03-22

This is the code to reproduce what I'm trying to understand:

First I have a class with a pointer as attribute and its contructor set to default.

class PointExampl {
private:
    int* p_ = new int;

public:
    PointExampl() = default;
    int* returnp() {return p_;};
};

This is the main.cpp:

int main() {
    std::vector<std::vector<PointExampl>> vec;
    vec.resize(10, std::vector<PointExampl>(10));

    for(int i = 0; i < vec.size(); i  )
        std::cout << vec[i][0].returnp() << std::endl;
    return 0;
}

And this is the output:

0x1695c8e1920

0x1695c8e1920

0x1695c8e1920

0x1695c8e1920

0x1695c8e1920

0x1695c8e1920

0x1695c8e1920

0x1695c8e1920

0x1695c8e1920

0x1695c8e1920

So this is what i dont get about std::resize . Shouldn't every first object of every row have a different pointer?

This is simplification of a problem that i got using std::vector for some class project about game of life. I was expecting to have a full matrix of new pointers but instead got a copy of the first row pointers to the other rows.

also im using c 14 if that has anything to do about it.

CodePudding user response:

From cppreference on insert() (emphasis mine)

Resizes the container to contain count elements.
If the current size is greater than count, the container is reduced to its first count elements.
If the current size is less than count,

  1. additional default-inserted elements are appended
  2. additional copies of value are appended.

Because you chose the second overload (one taking a value), it will copy the value (which is std::vector<PointExampl>) count times, which in turn means that it will make copies of elements inside vector.

It may be solved by actually making each vector (in a loop):

    std::vector<std::vector<PointExampl>> vec;
    vec.reserve(10); //optional
    for (int i = 1; i < 10; i  )
        vec.emplace_back(10); //not the clearest code, push_back would be easier to read but a tiny bit slower

Or with an algorithm like std::generate(), returning a new vector for each element. However, the proper solution would be to add a copy constructor (since your class manages a resource), and, consequntially, the Rule of Five

class PointExampl {
private:
    int* p_ = new int;

public:
    PointExampl() = default;
    PointExampl(const PointExampl& other) { 
        p_ = new int; 
        *p_ = *(other.p_);
    }
    PointExampl(PointExampl&& other) { 
        p_ = other.p_;
        other.p_ = nullptr;
    }
    PointExampl& operator= (const PointExampl& other) {
        *p_ = *other.p_;
        return *this;
    }
    PointExampl& operator= (PointExampl&& other) {
        if(this != &other) {
            delete p_;
            p_ = other.p_;
            other.p_ = nullptr;
        }
        return *this;
    }
    ~PointExampl() { delete p_; }
    int* returnp() {return p_;};
};

It's a crude example, and it certianly could be done better (e.g. using copy and swap idiom), but for the sake of simplicity it should do.

CodePudding user response:

std::vector<PointExampl>(10) creates a vector with 10 brand new, default constructed PointExampl objects, each with different pointers.

vec.resize(10, std::vector<PointExampl>(10)); takes that vector and copies it 10 times to the outer vector, meaning that every row in the top level vector is a copy of the same one, hence the identital 0th pointers.

The solution depends on what you're trying to achieve. You could perform new allocations when a PointExampl is copied by providing a copy constructor, or disable copy construction and individually create each element and push_back/insert to the appropriate vectors.

  • Related