It seems to me that if you're going to write your own vector class (or your own semi-duplicate of almost anything that's already widely available) you should try to not only make it correct, but add something new to the mix so your code isn't just a mediocre imitation of what's already easily available (along with preferably avoiding it's being a huge step backwards in any direction either).
For example, if I were going to support initializing a Vector from an array, I'd add a template member function that deduced the size of the array automatically:
template <size_t N>
Vector(T (&array)[N]) : data(new T[N]), size(N) {
std::copy_n(array, N, data);
}
This allows something like:
int a[]={1, 2, 3};
Vector<int> x(a);
...so you don't have to specify the size.
You've already heard about the rule of three. To avoid a step back from std::vector
, you almost certainly want to update that to the rule of 5 (or else use a smarter pointer class that lets you follow the rule of zero).
The straightforward way to do that is to implement a move ctor and move assignment operator:
Vector &operator=(Vector &&src) {
delete[] data;
data=src.data;
size=src.size;
src.data=nullptr;
src.size = 0;
return *this;
}
Vector(Vector &&src): data(src.data), size(src.size) {
src.data=nullptr;
src.size=0;
}
For convenience, you also almost certainly also want to include a ctor that takes an initializer list:
Vector(std::initializer_list<T> const &i) : data(new T[i.size()]), size(i.size())
{
std::copy(i.begin(), i.end(), data);
}
Finally, you just about need (or at least really want) to support an iterator interface to the contained data:
class iterator {
T *pos;
friend class Vector;
iterator(T *init): pos(init) {}
public:
iterator &operator++() { ++pos; return *this; }
iterator &operator--() { --pos; return *this; }
iterator &operator++(int) { iterator tmp(*this); ++pos; return tmp; }
iterator &operator--(int) { iterator tmp(*this); --pos; return tmp; }
T &operator*() { return *pos; }
bool operator!=(iterator const &other) const { return pos!=other.pos; }
};
iterator begin() { return iterator(data); }
iterator end() { return iterator(data+size); }
...then you want to add const_iterator
, reverse_iterator
and const_reverse_iterator
classes, and with them cbegin
/cend
, rbegin
/rend
and crbegin
/crend
to support constant and/or reversed iteration of the data.
Note, however, that most of this is just duplicating what std::vector
already provides. The only new thing we've added here is the ctor that takes an array and deduces its size automatically. At the same time, that is enough to provide a fixed-size array wrapper that (other than dynamic sizing) has approximate parity with std::vector
.