简体   繁体   中英

C++ 11 std::vector push_back method calls copy/dest so many times?

I am currently optimising my code and I have a question regarding the std::vector

I have a class MyClass which I have rewritten the copy/move constructor and their corresponding operators.

MyClass(const std::string& id, int x);
MyClass(const MyClass& other);
MyClass(MyClass&& other);
~MyClass();
MyClass& operator=(const MyClass& other);
MyClass& opratror*(MyClass&& other);

I created a vector and tried the following

std::vector<MyClass> vec;
MyClass a("A", 1);
vec.push_back(a);    //#1
vec.emplace_back("B", 2);    //#2
vec.push_back(MyClass("C", 3));    //#3

in #1 the copy constructor is called (which I know vector store by values so it copies a) in #2 it saves a copy constructor call only calls the constructor in #3 it calls the constructor and move constructor

But what I found is that, at #2, #3 where the vector is not empty, every push back / emplace / emplace_back triggers the copy/destroy of existing items.

in #2, it copies "A" and destroyed the existing "A" in #3, it does the same to "A" and "B"

it seems the vector resorts all the items whenever the array changes. Does it mean that using a class vector could make things inefficient? Is that the best solution to use a vector storing pointers so that there's not copies / destructors calls during resorting, only pointer copying?

Thanks

Not resorts, but reallocates. A vector is required, by contract, to store its values contiguously, like a plain array. The only way to guarantee contiguous storage is to allocate one chunk of memory. Once you got it, you're done. You can't make it bigger. All you can do is allocate a larger chunk and copy everything over and then delete the older smaller block of memory. That's what you're seeing.

A vector typically reserves some additional extra space, to accomodate new elements that might get added (so that this copying doesn't happen with every push_back), but when the vector is small, initially, only a little bit of extra space gets reserved for future growth, and this reallocation still happens quite often. But as the vector grows in size, more and more extra space gets reserved, and the reallocation happens less often.

If you know in advance how much values you are going to push_back (), you can use reserve() up front to preallocate the additional space up front, and minimize the reallocation.

If you know you're going to add ten more values to the vector:

vec.reserve(vec.size()+10);

If the vector already has at least ten more values it can accept without reallocating, then this does nothing. Otherwise the vector gets reallocated with enough extra space for at least ten additional values. The next ten push_backs are guaranteed not to cause reallocation.

You need to make your move ctor noexcept(false) ; otherwise in many cases it cannot safely be used.

Imagine having a vector of 10 elements in a buffer that fits 10. We resize it to fit 15, and move the first 3 of them.

Moving the 4th throws. How are we supposed to return the vector to a sane state?

Instead, the C++ standard demands that throwing move not be used, and it copies the 10 elements to the new buffer. When that succeeds, it can then destroy the old buffer.

With noexcept move, it can without fail move the 10 elements to the new buffer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM