7

I've been getting my head round c++ for a few months, and have been directed by google to stack overflow most of the time for c++ queries. I noted frequently exhortations of the type "why don't you use a vector", and was inspired to do just that.

So, primarily to get the minor benefits of automatic memory deallocation, and being able to write typed comparison functions for sorting. I switched an array of pointers to objects to being a vector. Now I thought (incorrectly it seems) that vectors could be used more or less like arrays, and hence I initialized thusly:

cluster  clusters[LOTS];
vector<cluster *> pclust;
pclust.reserve(numClust);
for (int i=0; i<numClust; ++i)
    pclust[i] = clusters + i;

No complaints from the compiler. Then some time later I need to sort the vector on some attribute of the cluster object. So:

std::sort(pclust.begin(), pclust.end(), ClusterCompareNumSegs);

Again no problems compiling. Except the vector doesn't get sorted. It turns out that vector.size() is zero, and of course my initialization should have been

pclust.push_back(clusters + i);

Now that's easy to fix, but I am confused, because the initial incorrect assignment was working. I successfully iterated through the vector - using the array syntax, like so:

for (clustind=0; clustind < numClust; ++clustind) {<br>
    cluster *cl = pclust[clustind];
    ...happily access *cl...

And it all worked fine. So I'm just wondering what's going on. Presumably in my initial assignments, I was trying to access elements not yet in the vector (I was trying to put them in), and the vector was throwing exceptions that I was ignoring. But nonetheless, when referencing the locations, the pointers were there. Can anyone provide enlightenment?

bandjalong
  • 483
  • 2
  • 6
  • 9
  • 2
    The standard library is pretty vast, and not particularly welcoming to the beginner, I suggest you read the documentation on at least the container types (http://en.cppreference.com/w/cpp) - it will save you much pain. – cmannett85 May 30 '12 at 09:34
  • Just to reiterate, my code is all fixed now, but I was just wondering – bandjalong May 30 '12 at 10:56
  • .. If anyone can explain while the operator [] worked as well as it did, even without the size being change. Just lucky with the compiler (gcc) implementation of vectors perhaps? – bandjalong May 30 '12 at 10:58
  • @user1425406: You fell unfortunately in one of the traps of C++. There are two ways to get a reference to an element: `operator[]` and `at()`. Both have similar interface, and the simplest way to do it `[]` is also the unchecked one, *because tripping beginners is fun*. – Matthieu M. May 30 '12 at 12:07

4 Answers4

11

vector::reserve doesn't change the size of your vector, it still contains only the 0 elements it was created with. What it does is make sure that the vector can potentially hold numClust without having to reallocate. See here.

What you want is to either declare the vector to have that size

vector<cluster *> pclust(numClust);

or to resize the vector

pclust.resize(numClust);
Benjamin Bannier
  • 55,163
  • 11
  • 60
  • 80
  • The reason I called reserve was because I knew how many elements I was going to add, and wanted there to be enough space so that reallocations weren't necessary while adding them (I really want the efficiency of arrays, and thought vectors were close enough). – bandjalong May 30 '12 at 10:43
  • ..But are you saying that calling resize would have allowed me to initialize them with the array syntax? If so that's good (I was afraid that the array syntax couldn't be used as an lvalue, which would be annoying). But I'm still wondering why the array was initialized, in that the data was there, but yet not initialized in that the vector thought it had zero size... – bandjalong May 30 '12 at 10:46
  • @user1425406: `operator[]` doesn't add elements to a vector. It returns a reference to existing elements. `resize()` does add elements to a vector. Be careful about the word "initialize". In C++ that has a very specific meaning, and for class types that means calling a constructor. `a[3]=b` calls the assignment operator, and that requires an already-initialized object on the left hand. – MSalters May 30 '12 at 11:21
  • @user1425406: `operator[]` also does not check if you are accessing beyond the end of the vector (just like an array). But if you want to validate some code it is sometime usefull to use `at()` while testing. This is exactly like `operator[]` but does check that the index is in the correct range. – Martin York May 30 '12 at 11:28
5

std::vector::reserve requests that the capacity of the allocated storage space for the elements of the vector container be at least enough to hold n elements. It doesn't resize the vector, that's what std::vector::resize does.

Replace pclust.reserve(numClust); with pclust.resize(numClust);.

Alternatively you could remove pclust.reserve(numClust); call and change construction of this vector to: vector<cluster *> pclust(numClust); which yields same result.

I also suggest you to have a look at this question: std::vector reserve() and push_back() is faster than resize() and array index, why? :)

Community
  • 1
  • 1
LihO
  • 41,190
  • 11
  • 99
  • 167
  • Replacing reserve with resize would not solve the goal (I think) of making sure that exactly the right of amount of memory is allocated and no more (which is what I had in mind, as well as avoiding reallocs). And I can't call the constructor with the size, because I don't know the size at the time the constructor is called. But that's OK, it's all good now, I just don't understand why my (wrong) code worked as well as it did. – bandjalong May 30 '12 at 11:01
  • @user1425406: Why would `resize` allocate more memory than `reserve` ? Difference between these two is that resize doesn't only "reserve" memory but also construct these elements, thus you are able to access them by using `at()` or `[]`. If performance is what you are looking for, then stay with `reserve` + `push_back`. – LihO May 30 '12 at 11:08
  • I thought reserve was guaranteed not to allocate more than the specified amount, and that resize wasn't. But looking now, I can't see why I thought that. So OK, resize would work (but as you suggest, reserve and push_back is the fastest). Basically I want the compiler to generate the same code as what it would if you just did the obvious loop to initialize an array.. – bandjalong May 30 '12 at 12:16
0

operator[] used with vector returns reference to element on index-position. BUT, you haven't initialized vector with any values, so it was empty.

Altough you did a pclust.reserve(numClust), but it only tells that size of the vector will change soon and it allocates storage space without changing vector's size yet.

joval
  • 466
  • 1
  • 6
  • 15
  • But that's the mystery, [] was working (after a fashion), all those initial values I put in (while the vector remained at size zero), were successfully retrieved and dereferenced subsequently. – bandjalong May 30 '12 at 10:54
0
cluster  clusters[LOTS];
vector<cluster *> pclust(numClust);
for (int i = 0; i < numClust; ++i)
  pclust[i] = clusters + i;

But this mean you are still using an array to store clusters. Can't you make clusters a vector ?

vector<cluster> clusters(LOTS);
log0
  • 10,489
  • 4
  • 28
  • 62
  • Yes I could, but I can't see an advantage to it being a vector (I can't sort that anyway, I need to serialize data structures with indicies into it...). – bandjalong May 30 '12 at 10:53