Run time type information rtti and casts
Advertisement
This presentation is the property of its rightful owner.
1 / 22

Run-time type information (RTTI) and casts PowerPoint PPT Presentation

Run-time type information (RTTI) and casts. Consider classes for components and windows: class Component { ... virtual void draw() {} }; class Window: public Component { ... virtual void addComponent(Component *c); virtual list<Component*> getAllComponents(); };.

Related searches for Run-time type information (RTTI) and casts

Download Presentation

Run-time type information (RTTI) and casts

An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -

Presentation Transcript


Run time type information rtti and casts

Run-time type information (RTTI) and casts

  • Consider classes for components and windows:

  • class Component {

  • ...

  • virtual void draw() {}

  • };

  • class Window: public Component {

  • ...

  • virtual void addComponent(Component *c);

  • virtual list<Component*> getAllComponents();

  • };


Run time type inference

  • CS 213

  • Fall 1998

  • Run-time type information (RTTI)

  • and casts


Run time type inference

  • class Component {... virtual void draw() {}};

  • class Window: public Component {

  • ...

  • virtual void addComponent(Component *c);

  • virtual list<Component*> getAllComponents();

  • };

  • class Border: public Component { ... };

  • class Menu: public Component { ... };

  • class CheckBox: public Component {

  • ...

  • virtual void resetToDefault();

  • };

  • Suppose we want to call resetToDefault for all the CheckBoxes in a window. We can use getAllComponents to find all the components, and then we can loop through the components to reset each CheckBox.


Run time type inference

  • class Window: public Component {

  • ...

  • virtual void addComponent(Component *c);

  • virtual list<Component*> getAllComponents();

  • };

  • void resetAllCheckBoxes(Window *w) {

  • list<Component*> l = w->getAllComponents();

  • for(list<Component*>::iterator i = l.begin();

  • i != l.end(); i++) {

  • Component *c = *i;

  • ... // is c a CheckBox???

  • }

  • }

  • But what do we do for each component c? If c is a CheckBox, we’d like to call resetToDefault. But how do we know whether c is a CheckBox?


Run time type inference

  • First, let’s try to avoid the problem. Maybe all components should have a resetToDefault function in them:

  • class Component {

  • ...

  • virtual void draw() {}

  • virtual void resetToDefault();

  • };

  • So then we can call resetToDefault for each component in the list.

  • But this is lousy - why should, say, a Border have a resetToDefault function? What if Component was written by a different company than CheckBox? Why should the authors of Component have to be aware that some derived classes will have a resetToDefault function?


Run time type inference

  • perhaps Window could be a template class:

  • template <class T>

  • class Window: public Component {

  • ...

  • virtual void addComponent(T *c);

  • virtual list<T*> getAllComponents();

  • };

  • So if we create a Window<CheckBox>, then we know every component in the window supports the resetToDefault function.

  • But this isn’t appropriate here - a Window<CheckBox> can only support CheckBoxes, and not Menus or Borders. So this is too restrictive.


Run time type inference

  • The two approaches just described avoid the need for determining types at run-time:

  • Add more functions to the base class: if every component supports resetToDefault, then we don’t have to figure out which components are CheckBoxes and which aren’t. We can just call resetToDefault for every component in the window.

  • Use templates: if every component in the Window is a CheckBox, then every component must support resetToDefault

  • In general, these are the preferred technique to use C++.

  • But sometimes, neither of these techniques is adequate. This is especially true when we hand an object (like a CheckBox) to a system that someone else wrote (like Window), and this system hands our object back to us with less type information. In this case, we lost the exact type of the CheckBox, and we know only that it is a Component.


Run time type inference

  • To deal with this, C++ adds a special type of cast that checks the type of an object at run-time:

  • ...

  • Component *c = ...;

  • CheckBox *checkBox = dynamic_cast<CheckBox*>(c);

  • if(checkBox != NULL) checkBox->resetToDefault();

  • dynamic_cast<CheckBox*>(c) examines the type of c at run-time, and if c is a pointer to a CheckBox (or if c is a pointer to an object of a type derived from CheckBox), then the cast returns a pointer to the CheckBox.

  • Otherwise, the cast returns NULL, indicating that c does not point to a CheckBox.


Run time type inference

  • Note that dynamic_cast is very different from a C-style cast:

  • Component *c = ...;

  • CheckBox *checkBox = (CheckBox*)(c); // C-style cast

  • checkBox->resetToDefault();

  • The C-style cast assumes that c points to a CheckBox, whether it actually does or not! No run-time check is performed. So if c points to a Border or Menu, the above code will probably crash.


Run time type inference

  • C++ defines four different cast operators:

  • dynamic_cast

  • static_cast

  • reinterpret_cast

  • const_cast

  • dynamic_cast is the safest of these. It can only be used on classes that have virtual functions. It cannot be used, for instance, to cast a double to an int.


Run time type inference

  • dynamic_cast can be used to navigate a class hierarchy:

Window

Window_with_border

Window_with_menu

Clock


Run time type inference

  • dynamic_cast can be used to navigate a class hierarchy:

  • An upcast requires no explicit cast:

  • Clock *c = ...;

  • Window_with_border *w = c; // No cast needed

  • This is because all Clocks are Window_with_borders.

Window

Window_with_border

Window_with_menu

Clock

upcast


Run time type inference

  • dynamic_cast can be used to navigate a class hierarchy:

  • A downcast requires an explicit cast:

  • Window_with_border *w = ...

  • Clock *c = dynamic_cast<Clock*>(w); // Explicit cast needed

  • This is because not all Window_with_borders are Clocks.

Window

Window_with_border

Window_with_menu

Clock

downcast


Run time type inference

  • dynamic_cast can be used to navigate a class hierarchy:

  • A crosscast also requires an explicit cast:

  • Window_with_border *wb = ...

  • Window_with_menu *wm =

  • dynamic_cast<Window_with_menu*>(wb);//Explicit cast needed

  • This is because not all Window_with_borders are Window_with_menus.

Window

Window_with_border

Window_with_menu

crosscast

Clock


Run time type inference

  • Upcasts, downcasts, and crosscasts can change the address that a pointer points to. For instance, after the crosscast, wm and wb point to different locations within a single Clock object:

Clock data

Window data

wb

Window_with_border data

ptr

wm

Window_with_menu data

ptr


Run time type inference

  • dynamic_casts on pointers return NULL if the cast fails:

  • Component *c = ...;

  • CheckBox *checkBox = dynamic_cast<CheckBox*>(c);

  • if(checkBox != NULL) checkBox->resetToDefault();

  • dynamic_casts can also be used on references:

  • Component &c = ...;

  • CheckBox &checkBox = dynamic_cast<CheckBox&>(c);

  • If a dynamic_cast fails on a reference, a bad_cast exception is thrown.


Run time type inference

  • static_cast isn’t as safe as dynamic_cast. It can do downcasts if you are absolutely sure the cast is correct:

  • Window_with_border *w = ...

  • Clock *c = static_cast<Clock*>(w);

  • However, no run-time check is performed, so this may lead to a crash if w isn’t really a Clock. It’s safer to use dynamic_cast.

  • static_cast cannot be used for crosscasts.


Run time type inference

  • So why would anyone use static_cast?

  • It’s somewhat faster, since no run-time check is performed. (But this shouldn’t be your major concern, since you don’t use casts very often anyway.)

  • It can also be used to cast from void*, which is useful when interfacing to old C code:

  • void *v = ...;

  • Clock *c = static_cast<Clock*>(v);

  • dynamic_cast can’t be used here, since void* isn’t necessarily a pointer to a class with virtual functions.


Run time type inference

  • reinterpret_cast is used for low-level bits hacking, such as converting a pointer to an integer:

  • Component *c = ...

  • int i = reinterpret_cast<int> c;

  • This is highly implementation dependent, and very dangerous.


Run time type inference

  • Finally, const_cast is used to ignore the constness of a value:

  • const int *i = ...;

  • int *k = const_cast<int*>

  • This isn’t very tasteful, but is sometimes necessary when interfacing const-aware code with const-unaware code.


Run time type inference

  • You should use casts rarely. If you do need to use a cast, remember that some casts are safer than others:

  • const_cast: safe (not necessarily tasteful, but sometimes necessary)

  • dynamic_cast: safe, as long as you check the result to see if it is equal to NULL.

  • static_cast: dangerous – needed for cast from void*, otherwise dynamic_cast is usually better

  • reinterpret_cast: really dangerous, needed only for low-level bits hacking

  • The most dangerous of all is the C-style cast, which is deprecated in C++. A C-style cast is an unpredictable mixture of const_cast, static_cast, and reinterpret_cast.


Run time type inference

  • dynamic_cast used run-time type information about an object. This run-time information is also available to programmers directly. The typeid operator returns a type_info object for an expression or type:

  • type_info &ti = typeid(e);

  • type_info contains a name function to print the name of a type. This can be useful for diagnostics:

  • Component *c;

  • cout << typeid(*c).name() << endl;

  • Like dynamic_cast, typeid only works for classes with virtual functions.


  • Login