0

So far I have been using dynamic casting. But this comes with it's pros and cons. It seems that is a good thing NOT to use this too much. The examples on this topic, that I have found, are usually with classes that have little differences. But in my case, the "child" classes have very little similarities.

The code in this post is NOT from the project. It's only used for examples.

I am making a trading system for a game and there will be many more systems in the project. There are many different items that do many different things- equipment, modifications, resources. No matter how different they are, they all have a price and they can all be putted in an inventory, no matter what they are. But this is where are the similarities end, including the overridden methods.

Afterwards the different items are used in completely different ways. At first the different types of items were sorted in separate arrays of pointers from different types- one for the equipment, one for the modifications, e.t.c. To put something in an inventory I only use a single method- addToInventory(Item* item) . Since the item must be placed in the right array, I use dynamic casting- I convert Item* item to (for example) Equipment* equi, so I can add it to the Equipment array. I want to do it in the same method, because it's more intuitive and otherwise the different methods would have similar code.

addToInventory(Item* item)
{
    if (item->type == 'e')
    {
        Equipment* newEquip = dynamic_cast<Equipment*>(item);
        equipmentArr.add(newEquip);//thous arrays are dynamic- the reason I needed to make the conversion explained later
    }
    else if (item->type == 'm')
    {
        Modification* newMod = dynamic_cast<Modification*>(item);
        modificationArr.add(newEquip);
    }
    //and so on...
}

Later I would want to add a modification to a piece of equipment- Weapon::addMod(Modification* mod) . And in this method I use other methods and variables that are found ONLY in the Weapon class.

addMod (Modification* mod)
{//all are found ONLY in class Weapon
    mod[modCount] = mod; //an array of Modification* pointers
    modCount++;
    calcEfficiency();
}

But when I want to make the simple thing to print an inventory, I either have to copy-paste and edit some code for converting the pointers in the arrays, so I can pass them in the same printing method, or copy-paste and edit the same code for printing. There is a third option- to make the arrays to all arrays of pointers to Item objects. I tried the last option.

It got rid of the casting in addToInventory(Item* item), yay! But it caused the need to use casting EVERY time I need to call methods such as Weapon::addMod(Modification* mod) and in other places. Otherwise, I will need to put the casting within the method, but I want the method to explicitly take an Equipment* argument.

The project is still really early in development, so I don't know how much more I might need to use casting, so I can switch back and forth between different types of pointers when needed.

So, in a similar case, how should I switch between different types of pointers?

AlexSavAlexandrov
  • 858
  • 2
  • 13
  • 34
  • It sounds like virtual functions would fit your needs? – M.M Jun 04 '14 at 23:41
  • Also you don't need a cast to get a base class from a derived class – M.M Jun 04 '14 at 23:43
  • Adding more virtual functions will make the code require more 'if' statements for the different types, making it more complicated and more counter-intuitive. In the code I will be treating an Item like an Item, even though in reality I am treating it as a Modification. And yes, I know I don't need casting when switching from derived to base class. The problem is the other way- from base to derived. – AlexSavAlexandrov Jun 05 '14 at 10:21

1 Answers1

1

You may want to represent the traits (namely Equipment and Modification) of your (broad) Item implementations as pure virtual classes (i.e. interfaces). This way dynamic casting and dynamic cast checks for these interfaces is OK and will lower the noise to handle for actual implementations of Equipment and Modification.

Another way is to use the CRTP pattern and static_cast<Interface*> to have compile time checks for your interfaces.

Depends on your use case which way is more appropriate. As a rule of thumb:

  • Mostly static configuration => Do at compile time
  • More dynamic configuration (run time allocated instances) => Do at runtime
Community
  • 1
  • 1
πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190