Array Templates

Started by
7 comments, last by ElNoNombreHombre 20 years, 12 months ago
In responce to a post I saw somewhere else on the fourm, one of the posters said that having an array template is something not to be underestimated. So, a quick trip to my C++ For Dummies (man.. I should get a better C++ book) book finds me in the Template section, with an Array example. So, copy, play with, add a few things later, I have this:
  
// CArray.h

#ifndef __CARRAY_H__
#define __CARRAY_H__

#include <mem.h>

template <class T>
class CArray
{
        public:
        // (default) constructor

        CArray(int ArraySize=1)
        {
            // make sure that array is bigger than 0

            if(ArraySize < 1)
                throw "Error: Invalid Array Size";

            // set array size to desired size

            Size = ArraySize;

            //

            Data = new T[Size];
        }

        // copy constructor

        CArray(CArray<T>& CopyArray)
        {
            // delete data

            delete[] Data;

            // set to null, just to be safe

            Data = NULL;

            // get the new array size

            Size = CopyArray.Elements();

            // reallocate memory for array

            Data = new T[Size];

            // copy the data

            memcpy(Data, NewArray, Memory());
        }

        ~CArray()
        {
            // Delete the data

            delete[] Data;

            // set to null, to be safe

            Data = NULL;

            // no more array, so size is zippo

            Size = 0;
        }

        T& operator[](int Index) const
        {
            if(Index < 0 || Index >= Size)
            {
                throw "Error: CArray bounds exceeded";
            }

            return Data[Index];
        }

        operator T*() const
        {
            return(Data);
        }

        CArray<T>& operator=(const CArray<T>& CopyArray)
        {
            // delete data

            delete[] Data;

            // set to null, just to be safe

            Data = NULL;

            // get the new array size

            Size = CopyArray.Elements();

            // reallocate memory for array

            Data = new T[Size];

            // copy the data

            memcpy(Data, CopyArray, Memory());

            // return this array

            return(*this);
        }

        int Elements() const
        {
            return(Size);
        }

        int Memory() const
        {
            return(Size * sizeof(T));
        }

        protected:
        T* Data;
        int Size;
};

#endif
  
Well, I''m a little underwhelmed. As far as I (a very novice C++ person) can see, it seems to be a wrapper arround new/delete. Am I missing something (which I am most likeley)? Other than the new/delete convinience, is there any other benefit, or other function that should be included? I am NOT saying that this is worthless (I''m working on a string class as well, derived from this, and I think I''m starting to see how it''s benificial), however, I would just like someone who is more informed to fill me in. Thanks To all of those who think I''m just being contradictory: No I''m not! My First Game!!!"
To all of those who think I'm just being contradictory:No I'm not!My First Game!!!"
Advertisement
quote:Well, I'm a little underwhelmed. As far as I (a very novice C++ person) can see, it seems to be a wrapper arround new/delete.

Not really, it's more like a wrapper around an array. Array templates include all the functionality you'd want out of a built-in array, yet they work for any type. A wrapper around new/delete would be your own memory manager, or you own overloaded versions of those operators.

quote:Am I missing something (which I am most likeley)? Other than the new/delete convinience, is there any other benefit, or other function that should be included?

The biggest benefit is that you write the code once, and use it again for any type. The most accurate word to describe it is indeed "container," because it can contain any type. Another advantage is that you can include logic to have the array increase or decrease, without leaving that menial work up to the person using your object.

Array templates aren't too overwhelming, it's just that you need to be familiar with how templates work and getting the array behavior your want. The hardest part would probably be expanding the array, but even that isn't too difficult. Array templates are definitely a good exercise for beginners to put templates to use.

EDIT: At this time I thought I should point out the vector and string classes that are included in the standard library. They are already-implemented array and string objects respectively. Of course, writing your own is a good learning experience, and should be a lot of fun (if you really like programming!)

[edited by - Zipster on April 20, 2003 1:48:42 AM]
Ok, I''ll rail your code:

Throw a standard exception instead of char*''s, e.g. std::logic_error

An array size of 0 is well-defined, new T[0] is well-defined.

CArray(CArray& CopyArray)
is wrong
CArray(const CArray& CopyArray)

Your copy-ctor is not exception safe

You array class does not have value semantics (it is not default constructable)

T& operator[](int Index) const
has the wrong semantics
const T& operator[](int Index) const
T& operator[](int Index)

operator T*() const
has the wrong semantics, and should probably be made into an explicit function call
operator const T*() const
operator T*()
- or -
const T* ptr()const;
T* ptr();

Your assignment operator is not exception safe and dies if the same array is assigned to itself.


That''s why writing an array template is not trivial.
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
Yikes, you forgot to insult his mother! I wouldn''t call a templated array non-trivial, but I guess you just have to know what you''re doing before you start.

Another reason why to use vector.
OK... I think a clarification is in order here.

I did not mean WRITING it was trivial (heck, I never claimed it to be trivial in the first place)--I figured it was more than it looked, and thats why I came here, to find out what the big deal is with them, and why you'd use it over

var_type VarName[Howevertheheckmanyyouwant];

That's ultimately my question.

By the way, I'm still claming to be very novice, so don't rail against my code like I claimed that I'm some |337 h4x0r or something , although I did expect someone was going to take a sledgehammer to it.

quote:
Throw a standard exception instead of char*'s, e.g. std::logic_error


*whines*But I LIKE my char*'s

OK, fine, but I don't have much expirience with the std stuff, so I'll do that once I'm done with this class.

quote:
An array size of 0 is well-defined, new T[0] is well-defined.


Really? Didn't know that one.
(man... really really need a better C++ book)

But what the heck does it do then... or are you gonna go make me look ?

quote:
CArray(CArray& CopyArray)
is wrong
CArray(const CArray& CopyArray)


Grr... I remembered that for everything else that mattered for... can't believe I forgot that one.

quote:
You array class does not have value semantics (it is not default constructable)


But it is, thats what the default argument is there for

Ok ok, I'll fix it. I guess assume the want an array size 1 then?

quote:
T& operator[](int Index) const
has the wrong semantics
const T& operator[](int Index) const
T& operator[](int Index)


Heh, guess you want me to fall on one side of the fence or the other, right

I guess you mean one or the other (of the bottom two)? I guess I can see how it would be able to tell which to use if I did both--compiler'd have to figure it out based on the call's use, right?

quote:
operator T*() const
has the wrong semantics, and should probably be made into an explicit function call
operator const T*() const
operator T*()
- or -
const T* ptr()const;
T* ptr();


Same deal here, however, my goal is to make it look and act like a REAL array, so an extra function is out of the question here.

quote:
Your copy-ctor is not exception safe
---
Your assignment operator is not exception safe and dies if the same array is assigned to itself.


Egad. Thanks for pointing that out. And the exception safe thing... that would be catching new/delete messing up?

OK I guess that covers it.
Thanks for the responces, any more would be welcome.

Guess I should virtual the destructor too before someone yells at me for that as well

Edit: OK, apparently we don't like CAPITAL quotes...


To all of those who think I'm just being contradictory:
No I'm not!
My First Game!!!"

[edited by - ElNoNombreHombre on April 20, 2003 12:18:51 PM]

[edited by - ElNoNombreHombre on April 20, 2003 12:19:30 PM]
To all of those who think I'm just being contradictory:No I'm not!My First Game!!!"
You could look into std::vector<>, which allows you to grow & shrink the array at runtime.

Anyway, all that code in the array template? Isn''t it nicer to have to write and debug it only once, instead of having to put all that code into each place in your program where you do those operations?
If it''s just a wrapper, there may not be much point. However, consider something like the standard library vector class - it''s like an array, yet it can be resized, grow on demand, used with or without bounds checking, and it works with all the standard library algorithms.

Of course, even just a plain wrapper may be nicer than a raw array - conventional copy semantics, for instance, and at least you can query it for its size ...
To the AP, yes, yes I could.

Except I'd like to take a crack at it first

[edited by - ElNoNombreHombre on April 20, 2003 12:21:24 PM]
To all of those who think I'm just being contradictory:No I'm not!My First Game!!!"
quote:
...my goal is to make it look and act like a REAL array, so an extra function is out of the question here.


Well, it''s not an array, so you should only try to immitate it to a reasonable extent. By giving access to internal data, it is possible to modify it without CArray knowing about it, which could, and probably will, be a problem.

quote:
And the exception safe thing... that would be catching new/delete messing up?


That depends. The main issue is that you don''t know the behaviour of T. It could throw when assigning for example. In your copy constructor, Data is deleted first. If any other operation after that throws, it will not be possible to recover it. Exception safety menas giving a guarantee that CArray is at least destroyable (don''t leak or crash) if an exception is thrown. You can widen the guarantee so that an exception will not affect you array at all. It''s a tricky business, writing exception safe code, at least in my opininon.

quote:
Guess I should virtual the destructor too before someone yells at me for that as well


Not necessarily. That is up to you. It''s more likely that you will use delegation instead of inheritance, when reusing the array.

Regards Mats

This topic is closed to new replies.

Advertisement