Vector class

Pages: 1234
Ah, I see. I interpreted the T as referring to a type different from the vector's element type.
Fixed some of the issues that have been talked about. Still no copy control, and still can't be used with objects with no default-constructor. Working on that currently. Let me know what you think so far:
https://sourceforge.net/projects/datastructs/

I wasn't sure if I should throw exceptions to the user, or handle them at this level. Currently I'm catching, but not really doing anything. One question, if the allocation in the constructor fails, what will happen to the object?

I have a few commented questions throughout this, if you don't mind checking out for me.
I wasn't sure if I should throw exceptions to the user, or handle them at this level.

Throw them. Exception handling strategies don't belong in general library code, and seeing as you don't have a strategy for handling them.. definitely throw them.


One question, if the allocation in the constructor fails, what will happen to the object?

An exception should be thrown so the object is not constructed.

Again, for exception safety, take a look at http://www.stroustrup.com/except.pdf
it uses a vector implementation as example.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
template <typename T>
void MyVector<T>::reallocate()
{
    try
    {
        T* temp = new T[capacity_ * 2]; //(a)
        for(unsigned int i = 0; i < size_; i++)
        {
            *(temp + i) = *(data + i); //(b)
        }
        delete[] dat
        data = temp;
        capacity_ *= 2;
    }
    catch(std::bad_alloc& e)
    {
        std::cout << e.what() << "\n";
    }

    std::cout << "Reallocation successful.\n";
}
Lines (a) and (b) may thrown an exception.
If (b) also throws `bad_alloc' you may not be able to tell which one are you catching.

For (b) you need to deallocate the buffer, and destroy all the objects that were `created'.
Edit: as you are not using placement new, all the objects are already constructed.
I suppose that you could simply delete[] temp which will release everything properly. (you'll need to increase `temp' scope)


By the way, ¿why the `*(temp+i)' obfuscation?
Last edited on
For (b) you need to deallocate the buffer, and destroy all the objects that were `created'.
Edit: as you are not using placement new, all the objects are already constructed.
I suppose that you could simply delete[] temp which will release everything properly. (you'll need to increase `temp' scope)


You'll also need to increase the range of exceptions you catch, as you can't know what exceptions may be thrown by the contained type during construction. Something like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
template <typename T>
void MyVector<T>::reallocate()
{
    T* temp = new T[capacity_ * 2]; //(a)

    try {
        for(unsigned int i = 0; i < size_; i++)
            *(temp + i) = *(data + i); //(b)

        delete[] dat
        data = temp;
        capacity_ *= 2;

    }
    catch(...)
    {
        delete [] temp ;
        throw ;
    }
}
Doesn't throw; terminate the program right then and there, not rethrow?
Last edited on
Last edited on
1
2
        for(unsigned int i = 0; i < size_; i++)
            *(temp + i) = *(data + i); //(b) 


1
2
3
4
5
#include <algorithm>

// ...

        std::copy(data, data + size_, temp);

closed account (o1vk4iN6)
Don't really want to make a copy though, you want to move the resources if possible like the buffer for a string.

1
2
3
4
5
#include <algorithm>

// ...

        std::move(data, data + size_, temp);


Didn't know move had an iterator overload :P.
@ xerzi: what is the benefit of using std::move() instead of std::copy() in this case?
Throwing the exceptions makes much more sense, you're right. I have like no experience actually working with it though, so if I throw an exception out of the catch block like cire has shown above, I'm assuming the exception gets thrown to the user of the class? I read somewhere that it'll just get thrown into the next scope, but I don't feel like that's true.

By the way, ¿why the `*(temp+i)' obfuscation?

I wanted to gain some speed over using indices.

Lines (a) and (b) may thrown an exception.
If (b) also throws `bad_alloc' you may not be able to tell which one are you catching.

That's a good point. But would it matter? Either way it would lead to an inconsistent state. And I think this might get resolved when I finish writing my allocator class and use that.

what is the benefit of using std::move() instead of std::copy() in this case?

I second this. And also, are either of them going to be faster than direct pointer usage, like I'm doing right now?

EDIT:
Also, what is the point of this? I pulled it out of a book, and I don't understand why I need both:
1
2
3
4
5
6
7
8
9
10
11
template <typename T>
T& MyVector<T>::operator[](const unsigned int index)
{
    return *(data + index);
}

template <typename T>
const T& MyVector<T>::operator[](const unsigned int index) const
{
    return *(data + index);
}
Last edited on
ResidentBiscuit wrote:
I read somewhere that it'll just get thrown into the next scope, but I don't feel like that's true.
Hold on...are you saying you don't understand what a call stack is?

ResidentBiscuit wrote:
I don't understand why I need both
You need the non-const version so elements can be modified from outside the vector, and you need the const version to be able to access elements from a const perspective of the vector.
closed account (o1vk4iN6)
You are creating a copy then destroying the source, why wouldn't you try and save those resources ? If you have a vector of std::string you'll be reallocating and copying their buffers. With std::move you'd only be transferring the pointer, so no allocating and copying of the buffers.

I wanted to gain some speed over using indices.

There is no difference ... They do the same thing only that way is less readable.

are either of them going to be faster than direct pointer usage

It's just a way to keep your code clean and readable as well as not having a for loop everywhere doing the same thing.

what is the point of this? I pulled it out of a book, and I don't understand why I need both


It's for when the object is const, it'll use the second operator (the one with const appended to it) so that a const element is returned as well so that the values can't be modified to maintain the const of the vector.
1
2
const std::vector<int> a( 1 );
a[0] = 10; // compile error 

Last edited on
> I wanted to gain some speed over using indices.
The problem is... you are using indexes.
I suppose that you instead wanted
1
2
for(T *dest = temp, *source=data; dest < temp+size_; ++dest, ++source)
   *dest = *source;


About your `operator[]' http://cplusplus.com/forum/lounge/91192/2/#msg490573
quite bothersome as the code is exactly the same.
http://www.cplusplus.com/forum/general/40626/

which reminds me, you don't have iterators
Last edited on
@xerzi
I'm not particularly experienced with move semantics (just recently picked up a book on C++11) but wouldn't using std::move there be an issue since the buffers are different sizes?
@naraku: If that were so, then it would also be an issue with std::copy, and with the for loop, and with any other method of doing it you could think of.
closed account (o1vk4iN6)
^ what L B said

You are not moving the array, you're moving the elements of the array: http://www.cplusplus.com/reference/algorithm/move/
Last edited on
OK I see now, my thinking was that std::move moved the pointer of the old buffer replacing the newer one. I didn't think it through enough.
With std::move you'd only be transferring the pointer, so no allocating and copying of the buffers.

I think what you mean is that std::copy() would use copy operator= while std::move() would use move operator=, possibly resulting in lower memory usage while transferring the data?

Apart from that, wouldn't the overall memory usage be the same?
I don't think the goal is to use less memory at any given time, but rather to avoid the cost of copying data that could just be re-used.
Pages: 1234