Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Array of different objects

Status
Not open for further replies.

chrisw09

IS-IT--Management
Nov 23, 2005
22
US
I almost hate to post this as i'm new to c++ but i am having touble with pulling data out of an array.

I have 4 classes: Shape, circle, square, triangle. (they inherit from shape) Each class has unique functions. (eg. getRadius.)

I have an array: shapes which contains circles, squares, and triangles.

How do i tell what object type i am working with when i get it out of the array? Everything is coming out type shape and i cannot access the specific values of each class.
 
Well one easy thing to do is write a virtual WhatAmI() function in the base class that returns an enum or string that indicates what the real type is.

Another thing you can do is try to dynamic_cast it to each type and whichever cast doesn't return NULL (if it's a pointer) or throw an exception (if it's a reference) is the right type. BTW, this method of type detection is very bad.

You can also try using the typeid() function, but personally, I haven't had much luck with this.
 
All those options are generally bad for the same reason, the dynamic_cast isn't really that much worse than the others. That reason is that they are an indication of bad design.

In most cases, if you have a container of shapes, you only need to access methods that are appropriate for any shape.

A better solution is often to change the code so that there is a legitimate method inside the base class that will handle the situation for all types, not just one.

For example, pretend that the reason you need the getRadius function is to print the dimensions of the shape in an unrelated function. The solution isn't to check the type of the shape, and call getRadius if it is a Circle. Instead, a virtual printDimensions method should be added to the Shape class that is overridden by Circle (and the others) to print the dimensions.

I don't know what your reason is for needing to call a derived class specific function, but if it makes sense in any way to move the functionality to the base class, then that is a much better option than using WhatAmI() or dynamic_cast. If you cannot, then those are acceptable alternatives.
 
uolj,

I used your method and created a virtual printDimensions in my base class "shape" and extended it in circle to calculate the radius of the circle, which worked great.

My concern was with methods like isIsosceles() which would only apply to a triangle and wouldn't really make sense to have in the base class shape. I think i'll run with cpjust's WhatAmI().

I hated posting this question but i honestly couldn't find anything on the net about it. Surely i can't be doing something so stupid that it's never been addressed before? :)
 
Actually, it's been addressed many times. I've answered this question several times myself. Unfortunately, the internet is rather large, so finding anything on it can be rather difficult, especially when there isn't really an exact set of keywords that applies. So don't worry about it.

For your isIsosceles() thing, you can apply the same technique as you did for getRadius(). What function calls isIsosceles()? Why does that function call that on an element in an array of shapes? Whatever functionality needs to know whether the shape is isosceles is specific to triangles, and so you have to question why it is being called with your shape array and not as part of the triangle interface.

When designing public inheritance, it is best to focus on making sure that the derived classes work like a base class. In other words, anywhere that you have a base class, all the derived classes will work as well. If you are using the WhatAmI approach, then you have some objects that can check for Isosceles and others that cannot, and they all come from the same collection of base class pointers.

There are times when WhatAmI is appropriate. I've used that myself in both good designs and bad ones. The bad ones were hard to maintain, as I ended up with lots of switch statements and if/else clauses and tons of duplicated code. The good ones ended up using WhatAmI or dynamic_cast as part of a double dispatch system that is necessary when a function between two objects depends on the dynamic type of each (my example was sorting, where sometimes type mattered in the sort).

When I made the bad design, I didn't really know that it was that bad, but even if I did I probably would have done it anyway because it was an extension of existing code that would have been difficult to change. Now, after maintaining and expanding that code for years and having more headaches with the parts implemented via WhatAmI, I wish I could go back and change it. But, the best I have time for now is to not make the same mistake again.

Good luck!
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top