views:

1321

answers:

9

I know downcasting like this won't work. I need a method that WILL work. Here's my problem: I've got several different derived classes all from a base class. My first try was to make an array of base class. The program has to select (more or less at random) different derived classes. I had tried casting from a base class to the derived class, putting it in the array of the base, but obviously that didn't work. I was sincerely hoping for another method than simply sticking arrays of all the derived classes, because there could be quite a few derived classes. Is there any better way to do this that I'm just brainfarting on?

If y'all need code examples or more information, just let me know. It all makes sense to me, but It's late and it may not make sense to everybody else heh.

Any help is very much appreciated, guys.

+1  A: 

Use an array of BaseClass*, not an array of BaseClass. (Or pick a smart-pointer library to manage memory for you.)

Justice
+9  A: 

Casting from a base class to a derived class is a bad code smell in most circumstances.

Polymorphism, through the use of virtual methods, is a better approach.

The calling code should invoke a method using the base class pointer, allowing it to be dispatched to the appropriate implementation in the derived class.

I know this is a fairly generic answer, but without some code snippets as examples it's difficult to recommend a more specific answer in your situation.

LeopardSkinPillBoxHat
+11  A: 

Not sure what you mean. Sounds like you store the objects by value, and you you have an array of Base. That won't work, because as soon as you assign a Derived, that object will be converted to a Base, and the Derived part of the object is sliced away. But i think you want to have a array of pointers to base:

Base * bases[NUM_ITEMS];
for(int i=0; i<NUM_ITEMS; i++) {
    int r = get_random_integer();
    if(r == 0)
        bases[i] = new Derived1;
    else if(r == 1)
        bases[i] = new Derived2;
    // ...
}

If you ever haved worked with pointers, you will know it's a pain in the ass to manage them, espacially pass around and not lose them, since you will need to call delete on them to free the memory and call the destructor of the objects. You can use shared_ptr, and it will manage that for you:

shared_ptr<Base> bases[NUM_ITEMS];
for(int i=0; i<NUM_ITEMS; i++) {
    int r = get_random_integer();
    if(r == 0)
        bases[i].reset(new Derived1);
    else if(r == 1)
        bases[i].reset(new Derived2);
    // ...
}

Now, you can pass bases[x] to another shared_ptr, and it will note you have got more than one reference - it will call automatically delete if the last reference to the objects go out of scope. Ideally, you would also replace the raw array by std::vector:

std::vector< shared_ptr<Base> > bases;
for(int i=0; i<NUM_ITEMS; i++) {
    int r = get_random_integer();
    if(r == 0)
        bases.push_back(shared_ptr<Base>(new Derived1));
    else if(r == 1)
        bases.push_back(shared_ptr<Base>(new Derived2));
    // ...
}

Then you can pass the vector around, and don't lose the size of it, and you can dynamically add items to it on demand. Get the size of the vector using bases.size(). Read about shared_ptr here.

Conversion from a Base class to a Derived class should only be done when absolutely necessary. Normally, you want to use a technique called polymorphism, which means you call a function on the base pointer, but it will actually call a function defined in the derived class, having the same signature (name and parameters are the same type) and is said to override it. Read the article on wikipedia about it. If you really need to cast, you can do it like this for a raw pointer:

Derived1 * d = &dynamic_cast<Derived1&>(*bases[x]);

Using dynamic_cast ensures, that when you cast to the wrong type (i.e the type you cast is not the type that was created and assigned to the base pointer), you get an exception thrown by the operator. For the shared_ptr case, there are ways too:

shared_ptr<Derived1> d = dynamic_pointer_cast<Derived1>(bases[x]);
if(d) {
    // conversion successful, it pointed to a derived. d and bases[x] point still 
    // to the same object, thus share it. 
}
Johannes Schaub - litb
+1, his problem is slicing, pointers fix it, and smart pointers doubly so :)
orip
i like it. thanks a lot. i don't think polymorphism is feasible here because each derived class performs very unique functions and can have dozens of those, not to mention derived classes of it's own.
Cyprus106
If the derived classes are unique, you probably are mis-using inheritance.
Joe Schneider
+1  A: 

I think more information is needed. Why are you downcasting? are you doing the same thing to each derived class? If so you should be using an interface, or putting the code in a base class (or both).

For example if you've got an array of shapes (base), and you want to calculate their area, you could do the following:

    interface IShape
    {
       double GetArea();
    }

    class Square : IShape
    {
       double GetArea()
       {
          return sideLengh*sideLength;
       }
       ...
    }

    class FunnyShape : IShape
    {
       //do funny stuff
       ...
    } 

    ...

void calcTotalArea(List<IShape> shapes)
{
   double total = 0.0;
   foreach (IShape s in shapes)
   {
      total += s.GetArea();
   }
   return total;
}
Jesse Pepper
A: 

Alright, my apologies, I could have posted some code and a little more information on my desired end-result.

here's my example:

class bankAccount
{
   int money;
   bankAccount() {};

   int MoneyInAccount() {return money;};
}

class savingsAccount : public bankAccount
{
   int SavingsInterest();
}

class MoneyMarket : public bankAccount
{
    int CalculateAllPastReturns();
}

Then in my program I had an array of bankAccount[40] (it wasn't a pointer). I was hoping to be able to cast the BankAccount to SavingsAccount or MoneyMarket or CheckingAccount, etc. Each one having their own unique classes. From there, the program could have a collection of the different bank accounts and their unique information.

Of course, i know that trying to cast like that is bad, but i wasn't sure what to do.

I was hoping I was just blatantly missing something with pointers, etc. Anyways, I hope that was a little more specific. Thanks a lot for the help!

Cyprus106
You should put this sort of thing in as an edit to your question, rather than as an "answer".
Darron
whoops my mistake!
Cyprus106
Sooo... why not do what Darron suggested?
Aardvark
A: 

If you have an array of bankAccount[40] objects, you're never using your derived classes.

Casting to derived classes is very Java-ish because there you have the instanceof operator and it's all very natural. In C++ you can emulate it with reinterpret_cast(), but it's considered bad style.

What are you trying to do in the first place? Throw everything into one container?

Thorsten79
A: 

The problem here is that you are storing real BankAccount objects in your array, not SavingsAccount or MoneyMarket objects. You'll find that casting upwards in these cases will not work. Obviously you can try to use reinterpret_cast, but chances are it'll break. (imagine if savingsAccount had an additional member variable - interest rate - that would make it several bytes bigger than the base class)

What you should do is store pointers to BankAccount objects, these can then be whatever derived class you like - you allocate a savingsAccount, and put a pointer to it in your array. That works fine, the compiler happily keeps track of the object type, and you have allocated all the memory required for one of these derived types.

Now, once you have your savings or moneymarket accounts constructed correctly and stored somewhere, with references to them in your array, you can use dynamic_cast to convert the pointer held in the array to the real type - if you try to convert one of these pointers to an object of the wrong type, you'll get an exception. (obviously, you do not want to store a savingsAccount object, and then access it as if it were a moneyMarket object!), you'll only get a valid pointer to the object if the type is correct. You will need to turn on RTTI for this to work (ie the compiler will keep track of what type each object is).

The alternative way to achieve what you want, apart from using a good old C style union, is to put all the methods you will access into the base class, and call them directly on the base pointers you've stored in your array.

and after typing all that - litb says pretty much the same thing, only far better than I have - give him the tick!

gbjbaanb
+1  A: 

I still don't know what you're trying to do. You can't downcast objects in general, but rather pointers to objects. The usual way to do this is something like dynamic_cast(foo), where foo is of type bankAccount *. This will either give you a pointer to a MoneyMarket object or a null pointer.

However, downcasting to do an operation on an object is usually a mistake; that's usually better done by virtual inheritance and polymorphism. It is a way of sorting objects into different types.

If you want to know what the interest is, define interest as a virtual function in bankAccount, and account types that don't pay interest can just return zero. If you want to know which account types are MoneyMarket, downcasting is probably the way to go.

David Thornley
A: 

Trying to cast up/down the inheritance chain is generally just plain wrong. If you are doing this, then you are most likely not using inheritance and polymorphism correctly. If you are finding that you need to upcast or downcast then odds are good that you have a flawed class design.

As has been mentioned other answers, the way to go about this sort of thing is to have all the methods that you need defined in the base class. If a derived class doesn't support the method, then have it either be a no-op or have it return an error.

17 of 26
the problem is i can't define all of the methods in base because there can be dozens for each derived class, and derived classes from derived classes.
Cyprus106