Why use std::vector?

Started by
34 comments, last by ToohrVyk 16 years, 8 months ago
Hi, I really like std::vector, I love the concept of a dynamic array of course. However, my one gripe is the following snippet:


#include <vector>
#include <stdio.h>

static int constructs = 0;
static int destructs = 0;
static int copies = 0;
class Copyable
{
public:
    Copyable() { constructs++; }
    Copyable(const Copyable&) { copies++; }
    ~Copyable() { destructs++; }
};

int main(int argc, char* argv[])
{
    std::vector<Copyable> vec;
    for (int i = 0; i < 10000; i++) {
        vec.push_back(Copyable());
    }
    printf("%i constructs\n%i copies\n%i destructs\n", constructs, copies, destructs);
    return 0;
}



Prints out: 10000 constructs 34308 copies 34308 destructs Note that those precursor the scope exit; i.e. there would be total 44308 destructs after vector destruction. I find these copies and destructs to be excessive when it is easy to make something that can do similar with only 10000 total constructs and destructs with 0 copies; and it could still possess the same functionality as std::vector. Maybe I'm missing some advantage to this; but it seems a lot of overhead, particularly with user-defined classes! -Walt
Advertisement
That's why vector has the reserve() member function.
well, if you would create the vector with starting memory of size 10000, it would have only 10000 constructions and 10000 copies, or so.. :)
If that's not the help you're after then you're going to have to explain the problem better than what you have. - joanusdmentia

My Page davepermen.net | My Music on Bandcamp and on Soundcloud

Is it a bottleneck?

If not, don't worry about it. Unless you have highly specialized needs, you can't implement something with the same characteristics that will not copy it's contents the way vector does to resize. You can't simply memcpy the data, that isn't safe for non-POD types.

Besides, simple solution? reserve(), std::vector<Copyable*>. Maybe an allocator if you really need to. There are lots of ways to improve the performance of SC++L containers in sane ways.
To add to what's already been said, copying elements during resizing is the only sane way to grow the array, memcpy is not always suitable for custom types. Use reserve if you know how big it will need to be, or store pointers.
Quote:Original post by jpetrie
Is it a bottleneck?


A huge one, to be exact :) And reserve() only really works if you know how big you want your array, right?

Quote:You can't simply memcpy the data, that isn't safe for non-POD types.


Please explain this; it's not that I don't believe you, but how does the following form for say non-POD object A not work:

push_back(A());    //Constructs A inside vectorpush_back(A());    //Tests for size - resize required    //Allocate (malloc) size of new array    //memcpy() old array to new array    //free() old array    //Old objects should still exist in their proper form,     //as they were constructed validly, and all of their contents    //were properly transferred?    //Construct A inside vector


-Walt
Well if you yourself have concluded that std::vector isn't suitable, have you thought about std::list?

Have you thought about how you can predict the kind of size to reserve?
Previous thread on why memcpy() isn't a viable option for UDTs.
Change A() to std::string("Hey we have a long string!!!") and you'll find out, in the form of a crash. As soon as the type you are storing in the vector has a pointer to dynamic memory, you're going to run into problems when using memcpy. That's why standard containers use copy constructors and such, to ensure they work with every type(provided they implement a proper copy constructor, etc)
Quote:Original post by shadowman131
Please explain this; it's not that I don't believe you, but how does the following form for say non-POD object A not work:


There are two reasons to this. The most elementary one is that memcpy doesn't do what you seem to think it does.

memcpy is a function which copies a block of bytes from one location in memory to another. It doesn't copy PODs, it doesn't copy integers, it doesn't copy non-PODs, the only thing it can copy is bytes.

The C++ Standard lets you reinterpret a POD object or array of POD objects as a sequence of bytes, use memcpy on that sequence of bytes, and then reinterpret the destination sequence of bytes back as a POD object or array of POD objects. The C++ Standard explicitly forbids you from reinterpreting a non-POD object (or an array of such objects) as a sequence of bytes—attempting to do so against this interdiction will yield a sequence of bytes which may or may not be a complete representation of the object (i.e. some parts of the object might not appear in the representation). And, even then, the C++ Standard explicitly forbids you from reinterpreting a sequence of bytes as a non-POD object—attempting to do so will result in outright undefined behaviour, and your program may crash, do what you think it should do, overwrite random memory, or launch a nuclear missile at a cow ranch in Alaska.

The second reason is that it's not practical. Some objects may be doing something important with their position in memory—perhaps they registered themselves with a manager, or perhaps they are keeping a pointer or reference to one of their own members. When such objects are stored in a container, they should be notified that they are about to be moved around—something which a copy constructor and destructor will achieve with decent performance. In short, even if converting non-POD objects to a byte sequence worked (which it doesn't), your container will still cause bugs when self-referring objects are stored in it.

This topic is closed to new replies.

Advertisement