-1

Coming from a PHP background, I'm trying to learn C++, since I find it an interesting language. As a practice I want to create a simple Vector class using templates, which is not to hard. However, I run into one problem.

I have created the following template class:

template <typename T>
class Vector
{
public:
    Vector(int length);
    ~Vector(void);
    int getLength();

    T& operator[] (const int index);

private:
    T *_items;
    int _count;
};


template <typename T>
Vector<T>::Vector(int length)
{
    _items = new T[length];
    _count = length;
}

template <typename T>
T& Vector<T>::operator[]( const int index )
{
    if (index >= getLength() || index < 0)
        throw exception("Array out of bounds.");
    return _items[index];
}

All functions are implemented, but they're not relevant to my question, so I haven't copied them here.

This class works as expected, with one exception: If I want to create a vector of array's, it doesn't work. e.g.:

Vector<int[2]> someVector(5);

What I obviously want is that the _items property of the vector class will be an int[5][2]. However, since the compiler replaces the 'T' with 'int[2]', the _items property will be int[2][5] (or at least, that's what I understood from debugging, please correct me if I'm wrong). As a result, the [] operator doesn't work correctly anymore, and therefore this whole class is useless for arrays.

Is there a way to solve this problem, so that this class also works for arrays? And if that's not possible, is there a way to prevent this class being initialized with arrays?

Edit: Thanks for all your responses so far. However, I might have been not entirely clear with my question. First of all, I created this class to get used to c++, I know there is a std::vector class, and I also now that it's better to use a vector of vectors. That's not really the problem. I just want to understand templates better, and c++ in general, so I want to know how to deal with this type of problems. I want to be able to create classes which don't make the program crash. If I, or someone else, would use this class right now and tried to use primitive arrays instead of vectors for this class, at some point the program will crash since the array is wrong (Vector(y) becomes int[x][y] instead of int[y][x] internally). So I want a solution which either creates the correct array, or prevents the vector being initialized with arrays at all.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
Tiddo
  • 6,331
  • 6
  • 52
  • 85
  • 4
    Make sure that you have [a good introductory C++ book](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list). It is very, very difficult to learn to write good, correct, error-free C++ code without understanding the fundamentals of a language. – James McNellis Aug 20 '11 at 16:12
  • 1
    "using templates, which is not too hard" - "templates" and "not too hard" don't really fit in the same sentence if you're talking about making your own template classes/functions. It's tricky. – Mat Aug 20 '11 at 16:19
  • To add to Mat's comment: templates can be hard, especially if you want them to cater for non basic types (like your int[2] object). They are not a great way to learn C++, and this is not a great example to learn from given the existence of the excellent STL Vector object. – Pete855217 Aug 20 '11 at 16:25
  • 1
    "Rule of 3" learn it or watch your code die horribly in a field of burning fire. – Martin York Aug 20 '11 at 16:35
  • What exactly is not working when you use int[2]? Are you reading garbage values, getting access violation, or anything? – hamstergene Aug 20 '11 at 16:53
  • 1
    You don't `throw new exception("something")`, you must `throw exception("something")`. The exception object should be created on the stack, not allocated on the heap. – Praetorian Aug 20 '11 at 16:55
  • 1. The not to hard part was for using templates to create a Vector class, not for templates in general. 2. @praetorian -thanks, I still need to get used NOT to use the new keyword for creating classes. 3. Eugene - as I said in my original post above, I want to have an array of (in this case 5) int[2]'s, but I get an array of 2 int[5]'s. – Tiddo Aug 20 '11 at 18:36
  • @Martin - I know, but this is just a test class, it isn't used for any real application. – Tiddo Aug 20 '11 at 19:01
  • @James - I have looked for C++ books before, but I find it a bit difficult to find one: I have quite some experience with OO programming, especially in php, and I do understand most of the more unique concepts (like pointers and templates), but I'm not really capable yet of applying these concepts correctly. Is there one book you recommend most? – Tiddo Aug 20 '11 at 19:21
  • Of the four introductory book listed on the page I linked, I'd recommend the fourth. – James McNellis Aug 20 '11 at 20:13

3 Answers3

7

Quite simply, don't ever use the in-built primitive arrays. For anything. They suck, tremendously. Always use a class wrapper such as boost::array, if you don't have TR1 or C++0x both of which also provide an array class. A Vector<boost::array<int, 2>> will trivially compile.

Why not use primitive arrays?

They have hideous implicit conversions to pointers, forget their size at the drop of a hat, aren't first-class citizens (can't assign to them, for example), don't check their bounds, for example. boost::array<int, 2> is exactly a primitive array without crappy conversions, is fully generic- for example, your Vector template works fine with a boost::array<int, 2> off the bat, and it can do bounds checking in the at() function.

I'm not suggesting using a dynamic array instead. boost::array is not a dynamically sized array. It is a constant size, value type array, that will not convert to a pointer, it has functions for use in the Standard library like begin, end, size, and it can be treated like any other type- unlike arrays, which have a dozen special rules- as you've found out.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • Why should I never use the primitive arrays? I think that there are many situations in which a Vector class would be overkill, and a primitive array is sufficient, especially when the number of elements is know at compile time. However, it's more likely I'm wrong than you're wrong since I think you're more experienced, so could you also explain why I should never use them? – Tiddo Aug 20 '11 at 18:59
  • @Tiddo - It is not "never", it is "hardly ever". And the reason is that "They suck, tremendously". The built in arrays in C is a hack that comes back an haunts us all the time. They forget their size, they decay into pointers at the slightest wind, and they don't work with containers. Or with `new`. – Bo Persson Aug 20 '11 at 19:34
  • @Bo - Even worse, they don't know their size at all, and the only difference with references is that you pass them by value by default right? Please correct me if I'm wrong. – Tiddo Aug 20 '11 at 19:55
  • 1
    @Tiddo: References hold their size with arrays- when you refer to an array, the size must be known. I wasn't suggesting using a Vector instead of a primitive array- `boost::array` is implemented with a primitive array, it just doesn't suck. I will edit my answer. – Puppy Aug 20 '11 at 20:02
5

I see a few things wrong with this.

  1. If you are making a generic vector class, why deal with a vector of arrays rather than a vector of vectors?

Rather than defining a vector of type int[2], try making a vector of vectors of size 2 with something like this:

Vector<Vector<int>> vector(5);
for (int i = 0; i < 5; i++)
{
    vector[i] = Vector<int>(2);
}
  1. Rather than using a constant sized array, try using a pointer.

Rather than using a pointer to arrays of size 2, use a pointer to a pointer, and allocate two spaces for that pointer. Pointers are a lot easier to deal with in C++ because you can simply pass the address of the pointer rather than having to copy the entire array. It's generally very bad form to pass arrays.

  1. Add a default parameter to your constructor

Rather than declaring the constructor with Vector(int length);, try using Vector(int length = 0); so that if the user doesn't specify a length it will default to a size of zero.

Finally, are you aware that there is in fact an std::vector, or are you aware of this and simply trying to replicate it? Templates are known to be one of the hardest subjects of C++, good luck!

Andrew Rasmussen
  • 14,912
  • 10
  • 45
  • 81
  • Constant-sized arrays are significantly faster and safer than a double Vector, and should be used wherever possible. – Puppy Aug 20 '11 at 16:38
  • Although I didn't mention it in my original post, I know how to fix this using a vector of arrays. However, I was making this class because I want to understand c++ better, and I just wanted to know how to deal with arrays in templates. I am aware of the std::vector class, but as I said before, I created this class only as a practice. I like the pointer idea, however I've always learned that I should make classes in a way that even when you abuse the class in the worst way possible, the application shouldn't crash. So I do want to be able to disallow arrays for this template. Is that possible? – Tiddo Aug 20 '11 at 18:44
  • You cannot disallow certain types. Have you tried using that type with std::vectors and seeing what happened? – Andrew Rasmussen Aug 21 '11 at 02:06
0

The condition must say index >= getLength(), because getLength() is not an allowed index value too (the first element's index is 0, the last's is getLength()-1)

hamstergene
  • 24,039
  • 5
  • 57
  • 72
  • 1
    nice, but you shouldn't post this as an answer because it has nothing to do with the question. – Karoly Horvath Aug 20 '11 at 16:23
  • It's most probably the cause of his problem. – hamstergene Aug 20 '11 at 16:35
  • @Eugene: How can it work for ints but not int arrays if his indexing computations are off. – Puppy Aug 20 '11 at 16:37
  • `new` might have rounded the allocated block size up by 8 byte-boundary, which means 5 ints actually got 24 bytes, not 20, and going out of bound for 1 item did not do any harm. – hamstergene Aug 20 '11 at 16:51
  • You're right about this, but this wasn't part of the problem I had. The problem I have is that I get an int[2][5] instead of an int[5][2] (so the getLength() will give the wrong number anyway), which is quite logical. However, I want to find a way to either block primitive arrays from this class, or to be able to use this class with primitive arrays. – Tiddo Aug 20 '11 at 18:48
  • @Tiddo It's not possible, getLength() won't return 2 when T is int[2]. What's really happening is that you've corrupted the `_count` variable. Show the code that fills and read the array, and we'll find the bug. – hamstergene Aug 21 '11 at 11:03