Archived

This topic is now archived and is closed to further replies.

Roof Top Pew Wee

Using the [] operator overloading on a class.

Recommended Posts

Say I have the following:


template <class cType> class dArray
{public:

   cType arrayVariable[1024];

	cType operator[](int numToReturn)
	{
		return arrayVariable[numToReturn];
	}

}

class enemyPlane
{public:
   float f;

}

dArray<enemyPlane> ep;

// later, why can't I do this, assuming the variable is all created:


ep[0].f = 2.3f;

This isn't my exact code, but basically that's what I'm trying to do. Can you guys see what's wrong here? More specifically, I get error C2106: '=' : left operand must be l-value --Vic-- The future of 2D game development: Flat Red Ball

Share this post


Link to post
Share on other sites
I think the problem lies here:


cType operator[](int numToReturn)


When this function returns, the value of whatever happens to be in arrayVariable[numToReturn] is returned.

What you want is the ability to modify stuff in that array from the looks of it. So just have it return by reference, something like this:

cType & operator[](int numToReturn)





You fight like a dairy farmer.

Share this post


Link to post
Share on other sites
You want to return a refernce.
What your [] operator is doing is returning a temporary copy of arrayVariable[numToReturn].

You want
cType &operator[](int numToReturn)

Share this post


Link to post
Share on other sites
It is also advisable to overload the method, so that constant objects of your class can use the operator:

template <class cTyoe>
class dArray
{
public:
...
cType& operator[](int n) { return arrayVariable[n]; }
const cType& operator[](int n) const { return arrayVariable[n]; }
...
private:
...
cType arrayVariable[1024];
...
};

Now you can do things like so (assuming you have overloaded this sort of constructor):

float some_floats[100] = { ... };

...

const dArray<float> float_array( some_floats, 100 );

std::cout << float_array[0] << float_array[46] << float_array[82] << std::endl;

Whereas before you wouldn't have been able to invoke the index operator (operator[]()), as float_array is a constant object and the index operator wouldn't have guaranteed to not alter the object's members, and so it would not have been allowed to be invoked. Because of the keyword const at the end of the new overloaded method's definition, the user can index into the array type, as the method promises not to alter the object's contents. Have a look on Google if you don't understand.

Also, you might want to read up about the STL containers, std::vector inparticular, to give you some better ideas about implementing what I think that you are trying to do.

[ Google || Start Here || ACCU || STL || Boost || MSDN || GotW || MSVC++ Library Fixes || BarrysWorld || E-Mail Me ]

[edited by - Lektrix on July 4, 2003 8:59:48 AM]

Share this post


Link to post
Share on other sites
Well you question has been answeared in full - but it does worry me that you have no bounds checking.. ie your program would most likely crash horribly with this example ( or even worse just give you memory outside the array)

ep[1024] = new enemyPlane();

it may offcourse be that you just cut it to make the code smaller when posting here, how ever I thougth I''d give you an heads up...



/Please excuse my bad spelling - My native language is binary not english
|Visit me
\Take my advice - I don''''t use it...

Share this post


Link to post
Share on other sites
quote:
Original post by guppy
but it does worry me that you have no bounds checking.. ie your program would most likely crash horribly
Bound checking can be easily done with mod operator (%).
return arrayVariable[numToReturn % MAX_ARRAY];
quote:
ep[1024] = new enemyPlane();

But ep[1024] does not return a pointer to enemyPlane. I think Roof Top''s array class is for read only storage. So, it is safe.

Share this post


Link to post
Share on other sites
quote:
Original post by alnite
quote:
Original post by guppy
but it does worry me that you have no bounds checking.. ie your program would most likely crash horribly
Bound checking can be easily done with mod operator (%).



I''d rather have my program crash than wrap indices like that.


Why you shouldn''t use iostream.h - ever! | A Good free online C++ book

Share this post


Link to post
Share on other sites
quote:
Original post by siaspete
I'd rather have my program crash than wrap indices like that.
There was a time when I had a thought like yours. If my program had a bug, and the bug actually laid on indices, you might not notice the overbound index since it was wrapped up and went back to 0. However, I think it might be a lot better to see my program working but displaying weird results than crashing my customers' computers

After all, we are supposed to check the maximum index first before using any arrays.

[edited by - alnite on July 4, 2003 1:30:07 PM]

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
quote:
Original post by alnite
Bound checking can be easily done with mod operator (%).
<code>return arrayVariable[numToReturn % MAX_ARRAY];</code>

Unfortunately -1 % x == -1, so negative indices would crash it.

Share this post


Link to post
Share on other sites
quote:
Original post by alnite
After all, we are supposed to check the maximum index first before using any arrays.



Not if we expect the function to check the bounds.

quote:

However, I think it might be a lot better to see my program working but displaying weird results than crashing my customers'' computers



Really? If your program is made unusable by some strange bug noone can find the cause for, your program is useless as well.
Your program won''t crash the whole computer, only its own process (at least on somewhat sophisticated operating systems).

Plus, it doesn''t have to crash the program. It should throw an Exception, which should be caught somewhere. Makes debugging *way* easier...

At least that''s how I learnt it, and experienced it.

Besides, you''re supposed to test your programs thoroughly before delivering them


My Wonderful Web Site (C++ SDL OpenGL Game Programming)

I am a signature virus. Please add me to your signature so that I may multiply.

Share this post


Link to post
Share on other sites
quote:
Original post by randomZ
Not if we expect the function to check the bounds.

Uh...I was talking about the maximum index here, not boundaries. Function checks the index passed and see if it is out of bound. But we are the ones who pass that index, and how do we know whether we are passing indices not in the array? I wouldn''t stupidly do: for ( int i=0; i<10000000; ++i ) if I know that the maximum index is way lower than that.

Let''s say it actually happens, it''s human error. I rarely use exceptions to catch human errors. Don''t get me wrong, I don''t mean human errors as in spelling, typos, or even logic errors because they will all be automatically reported during compile time or run time. If you make a typo, your will get a "Syntax error." If you make a logic error, things go really weird. You don''t even need an exception there, you know it instantly.

In my opinion, exceptions are best used to report errors to end users because of the nature of its error handling: "jumps into a specific line of code regardless where the error occurs." At this specific line of code, we can use it to report critical error to users. Since there is only one block of code that does this (report to customer), managing it can be very easy.

Now, what about if we use it to report programming errors (in this example, index out of bound). Once we got this error message, what can we do? Even if we know that the exception is thrown from line 29 in array.cpp file, we still have to check other files to find out "why the hell do I pass index number 50000 here?? This array only has 50000 elements", and we will eventually end up scanning some blocks of code in other files to see if we actually made logic errors.

Frankly, index out of bound IS a stupid error. It''s not a type of error that must be reported unless you are designing a programing language, it''s the error that we are supposed to know and handle it ourselves as we type our code, not because an exception, that we ourselves write, is thrown to us. Why would I make an index out of bound exception? I should know it by myself. And checking bound is only to prevent this program from crashing, and if I could do it with % operator, why would I throw an exception??

Programming languages, such as Java or even STL, that does have index out of bound exceptions are made FOR programmers. If you notice the kinds of exceptions that those programming languages have, they are all programming related. We are their customers. Their customers are programmers. And when an exception is thrown, a message box appears telling us what kind of exception it is and all bunch of information that might help, because it is targeted to us, programmers. Now imagine the same kind of errors are displayed to typical computer users who have no idea what RAM is, you tell them "index out of bound," "unhandled exception..," "not enough memory," "error in accessing memory at 0x10a9e9ff" etc, they will be confused and won''t want to touch the keyboard again or maybe your programs.

quote:
Really? If your program is made unusable by some strange bug noone can find the cause for, your program is useless as well. Your program won''t crash the whole computer, only its own process (at least on somewhat sophisticated operating systems).

I was exaggerating there. I know it wouldn''t crash computers entirely, but I know my customers will get confused with an error like "Index out of bound" or even "Unhandled exception at 0x3290a38e" or the infamous blue screens because my customers are not programmers.

Share this post


Link to post
Share on other sites
quote:
Original post by alnite
Unfortunately -1 % x == -1, so negative indices would crash it.
Actually, there is no such thing called negative index, so my operator functions look like this:

MyClass& operator[] ( UINT index );

Share this post


Link to post
Share on other sites
quote:
And checking bound is only to prevent this program from crashing, and if I could do it with % operator, why would I throw an exception??

Because you're covering up the symptoms rather than trying to find the problem that caused them. There could have been a calculation error in some obscure location in your program that caused a chain reaction of invalid data, which eventually manifested itself in the form of an invalid index. Or it could be several errors across multiple portions of your program. If you simply cover this up, then not only will your program behave improperly, but you won't even know where the problem was. If you can't see the error in your ways then there is something seriously wrong with your idea of proper debugging. And if this code always generated improper indices, why would you settle for having your program behave improperly? I'm sure your customers won't be pleased if your program doesn't do what it's supposed to, either.

[edited by - Zipster on July 4, 2003 5:38:39 PM]

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
quote:
Original post by alnite
Actually, there is no such thing called negative index, so my operator functions look like this:

MyClass& operator[] ( UINT index );
And then the users of your class have to take the dreaded unsigned integer route. No thanks.

Share this post


Link to post
Share on other sites
quote:
Original post by Anonymous Poster
quote:
Original post by alnite
Actually, there is no such thing called negative index, so my operator functions look like this:

MyClass& operator[] ( UINT index );
And then the users of your class have to take the dreaded unsigned integer route. No thanks.


uhm.. you don''t like unsigned int?..

guess so, operator [] should have one parameter, and that one parameter is of type size_t (at least in the std:: namespace objects)..

guess what size_t is?!?


well, you''re just an AP for now anyways.. so spam:D

"take a look around" - limp bizkit
www.google.com

Share this post


Link to post
Share on other sites
quote:
Original post by Zipster
Because you''re covering up the symptoms rather than trying to find the problem that caused them. There could have been a calculation error in some obscure location in your program that caused a chain reaction of invalid data, which eventually manifested itself in the form of an invalid index. Or it could be several errors across multiple portions of your program. If you simply cover this up, then not only will your program behave improperly, but you won''t even know where the problem was. If you can''t see the error in your ways then there is something seriously wrong with your idea of proper debugging. And if this code always generated improper indices, why would you settle for having your program behave improperly? I''m sure your customers won''t be pleased if your program doesn''t do what it''s supposed to, either.
Yes, you are right, I only covers up the symptomps. But, showing up the symptomps doesn''t mean I can solve the problem easier either. Invalid indices usually happens in a way like you said, improper calculations or data. If I know that I passed an index not within the boundary (by a thrown exception), I still need to check the calculations and data that may cause the invalid index. The exception does nothing else but to tell me that I passed a wrong index.

However, if I could also tell that I passed a wrong index by observing that my program behaves differently (because of % operator), I will also need to check the calculations and data. We both end up doing the same thing. What''s the difference?

Share this post


Link to post
Share on other sites
quote:
However, if I could also tell that I passed a wrong index by observing that my program behaves differently (because of % operator), I will also need to check the calculations and data. We both end up doing the same thing. What's the difference?

The difference is that anything could cause your program to behave improperly (anything that manipulates your program's data really), not just invalid indices calculated at certain points in your program. If things start behaving strangely, just how sure are you that it was the indices that caused the problem? You aren't exposing any symptoms, so instead of knowing exactly where the problem occurred - like an exception or error message would tell you - you are stuck inspecting all aspects of your program for proper operation. And if you have similar gimmicks set up all over your code, it could take a while to pinpoint the problem. What if the indices are fine, but some of the code that manipulates the data is wrong? Or maybe there are problems with the code that gets the input, or even displays and formats the output? How would you know without exposing the errors as they happen? You wouldn't, and you'd have to check everything at least once, if not more, to make sure it works. That's the difference.

quote:
The exception does nothing else but to tell me that I passed a wrong index.

Yes, so you know exactly why your program is behaving strangely instead of having to check everything else that could potentially be causing a problem. It's simply a matter of isolating what's wrong, to make it easier to find the problem. Cause and effect isn't a one-to-one mapping when it comes to debugging.

[edited by - Zipster on July 5, 2003 2:45:08 AM]

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
quote:
Original post by davepermen
uhm.. you don''t like unsigned int?..
No. It makes simple calculations and comparisons more cumbersome when you have to keep in mind that the values can''t get negative.
if (a - b >= 0) //oops, always true
if (a >= b) //a working variant
Thus uint is non-intuivive. The double plus range is useless almost always and the lack of negative values can bite you in the ass. Why should I like it? You tell me.
quote:
guess so, operator [] should have one parameter, and that one parameter is of type size_t (at least in the std:: namespace objects)..

guess what size_t is?!?
I know. I never claimed I liked STL''s way of doing it.

Share this post


Link to post
Share on other sites
You have a point Zipster, but I still fail to see any reason to use exception for programming related problems. The closest thing I could think of is to display it on the debug window, but definitely not to throw an exception.

AP, there are cases where negative sign is unnecessary such as indices, but there are also cases where negative sign is important, like in your example. Obviously, I wouldn''t use signed int for indices, but I would definitely use it for math.

Share this post


Link to post
Share on other sites
Exceptions are merely a nice way of letting the programmer know of any problems, while at the same time allowing the program to take care of any necessary cleanup. It''s better than just letting the whole program crash and go to shambles, in other words. But even if it is just a message to the debug window, at least you are notified if and possibly when an out-of-bounds occured.

Share this post


Link to post
Share on other sites
quote:
Thus uint is non-intuivive.

Unsigned integers are highly intuitive for array indices. However, I also don't think you should make the parameter for operator[] unsigned because, again, it masks problems. If you happen to pass a negative number, due to some problem in the code, it will happily be interpreted as its positive equivalent, instead of allowing the function to perform a bounds check with the negative number, which will always fail. Most likely it will still be out of bounds after the conversion, since a literal interpretation of the sign bit would result in a huge number, but who knows how big an array you have.

[edited by - Zipster on July 5, 2003 4:24:08 AM]

Share this post


Link to post
Share on other sites
quote:
Original post by guppy
but it does worry me that you have no bounds checking

Why does it worry you? The reason that there is no bounds checking is because most of us are aware of the performance issues. You can just add a method to your class that performs bound checking, or, if you want to go to the extreme, you could derive an additional class. For example, the std::vector class's index operator (operator[]()) doesn't perform bounds checking, but it also has an additional method, at(), which does perform bounds checking. (There is also an overloaded const version of at().)

[ Google || Start Here || ACCU || STL || Boost || MSDN || GotW || MSVC++ Library Fixes || BarrysWorld || E-Mail Me ]

[edited by - Lektrix on July 5, 2003 8:00:12 AM]

Share this post


Link to post
Share on other sites