Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are there performance risks for using static_cast to deal with a vector of mixed (base & derived) objects? (aka "it this a dumb idea?")

Given a base class of gameObject and a derived class of animatedGameObject, I thought it may be good to store all of their instances in an std::vector. If the vector GameObjects is declared to be the base type of gameObject*, derived object instances require casting.

Example:

vector<gameObject*> GameObjects;

gameObject A* = new gameObject( ...init... ); 
animatedGameObject B* = new animatedGameObject( ...init... );

GameObjects.push_back(A);
GameObjects.push_back(B);

// to access the animatedGameObject functions:
static_cast<animatedGameObject*>(GameObjects[1])->functionUniqueToAnimated();

Being afraid as per usual, I turned to Scott Meyers (Effective C++, 3rd Edition), who writes on the subject:

Many programmers believe that casts do nothing but tell compilers to treat one type as another, but this is mistaken. Type conversions of any kind (either explicit via casts or implicit by compilers) often lead to code that is executed at runtime.

I've read through his Item 27: Minimize Casting twice, yet given my inexperience with this, I am struggling with my inability to answer a simple question "IS THIS A DUMB THING TO DO?"

I should mention that there are several reasons why it is a dumb thing to do that have nothing to do with invoking static_cast. The questions, in order of importance, are:

  1. Am I not seeing some possible risks with the use of static_cast in my example above?
  2. Are there better data structures than the std::vector for such approaches? (only if there's one that is obvious, I'm not asking you to do my research for me.)

This is my first time asking a question here, so apologies in advance, where necessary.

like image 654
ilzxc Avatar asked Jul 01 '13 03:07

ilzxc


2 Answers

static_cast is not the right tool for the job, unless you know that the pointer goes to an animatedGameObject and not a gameObject. What data structure are you using to store that information?

Determining the type of a derived object after a base pointer is the job of dynamic dispatch or dynamic_cast. In your example, the call GameObjects[1]->draw() should work with no cast because draw should be a virtual function. Otherwise you can use dynamic_cast< animatedGameObject & >( * GameObjects[1] ) to assert that the object is an animatedGameObject, and throw a std::bad_cast exception if it's not. (This would still require a virtual function in class gameObject , usually its destructor.)

But doing static_cast to a polymorphic derived type is a code smell.


Also you ask whether std::vector is a good data structure for this use case. It is, but not a vector of "naked" pointers. C++11 now provides "smart pointer" memory management classes which perform new and delete for you, rendering the actual operators all but obsolete. Look into std::unique_ptr for this case.

like image 62
Potatoswatter Avatar answered Nov 14 '22 21:11

Potatoswatter


  1. If gameObjects[1] is not animatedGameObject, application will (most likely) die horribly.
  2. If draw() is virtual method that is present in gameObject, conversion is unnecessary.

In general, casting base class to derived class is unsafe. In those situations it makes sense to use dynamic_cast. Dynamic_cast returns NULL, if conversion cannot be performed.

Are there better data structures than

Well, IF your gameObjects are not automatically deleted elsewhere, it might make sense to use something like std::vector<std::shared_ptr<gameObject> >. However, standard shared pointers may introduce hidden overheads (extra new/delete, in worst case scenario they might even introduce multithreaded mutex lock, IF they're designed to be thread-safe), so you should make sure those overheads are compatible with your goals.

like image 35
SigTerm Avatar answered Nov 14 '22 23:11

SigTerm