Follow Techotopia on Twitter

On-line Guides
All Guides
eBook Store
iOS / Android
Linux for Beginners
Office Productivity
Linux Installation
Linux Security
Linux Utilities
Linux Virtualization
Linux Kernel
System/Network Admin
Programming
Scripting Languages
Development Tools
Web Development
GUI Toolkits/Desktop
Databases
Mail Systems
openSolaris
Eclipse Documentation
Techotopia.com
Virtuatopia.com
Answertopia.com

How To Guides
Virtualization
General System Admin
Linux Security
Linux Filesystems
Web Servers
Graphics & Desktop
PC Hardware
Windows
Problem Solutions
Privacy Policy

  




 

 

Thinking in C++
Prev Contents / Index Next

Downcasting

As you might guess, since there’s such a thing as upcasting – moving up an inheritance hierarchy – there should also be downcasting to move down a hierarchy. But upcasting is easy since as you move up an inheritance hierarchy the classes always converge to more general classes. That is, when you upcast you are always clearly derived from an ancestor class (typically only one, except in the case of multiple inheritance) but when you downcast there are usually several possibilities that you could cast to. More specifically, a Circle is a type of Shape (that’s the upcast), but if you try to downcast a Shape it could be a Circle, Square, Triangle, etc. So the dilemma is figuring out a way to safely downcast. (But an even more important issue is asking yourself why you’re downcasting in the first place instead of just using polymorphism to automatically figure out the correct type. The avoidance of downcasting is covered in Volume 2 of this book.)

C++ provides a special explicit cast (introduced in Chapter 3) called dynamic_cast that is a type-safe downcast operation. When you use dynamic_cast to try to cast down to a particular type, the return value will be a pointer to the desired type only if the cast is proper and successful, otherwise it will return zero to indicate that this was not the correct type. Here’s a minimal example:

//: C15:DynamicCast.cpp
#include <iostream>
using namespace std;

class Pet { public: virtual ~Pet(){}};
class Dog : public Pet {};
class Cat : public Pet {};

int main() {
  Pet* b = new Cat; // Upcast
  // Try to cast it to Dog*:
  Dog* d1 = dynamic_cast<Dog*>(b);
  // Try to cast it to Cat*:
  Cat* d2 = dynamic_cast<Cat*>(b);
  cout << "d1 = " << (long)d1 << endl;
  cout << "d2 = " << (long)d2 << endl;
} ///:~

When you use dynamic_cast, you must be working with a true polymorphic hierarchy – one with virtual functions – because dynamic_cast uses information stored in the VTABLE to determine the actual type. Here, the base class contains a virtual destructor and that suffices. In main( ), a Cat pointer is upcast to a Pet, and then a downcast is attempted to both a Dog pointer and a Cat pointer. Both pointers are printed, and you’ll see when you run the program that the incorrect downcast produces a zero result. Of course, whenever you downcast you are responsible for checking to make sure that the result of the cast is nonzero. Also, you should not assume that the pointer will be exactly the same, because sometimes pointer adjustments take place during upcasting and downcasting (in particular, with multiple inheritance).

A dynamic_cast requires a little bit of extra overhead to run; not much, but if you’re doing a lot of dynamic_casting (in which case you should be seriously questioning your program design) this may become a performance issue. In some cases you may know something special during downcasting that allows you to say for sure what type you’re dealing with, in which case the extra overhead of the dynamic_cast becomes unnecessary, and you can use a static_cast instead. Here’s how it might work:

//: C15:StaticHierarchyNavigation.cpp
// Navigating class hierarchies with static_cast
#include <iostream>
#include <typeinfo>
using namespace std;

class Shape { public: virtual ~Shape() {}; };
class Circle : public Shape {};
class Square : public Shape {};
class Other {};

int main() {
  Circle c;
  Shape* s = &c; // Upcast: normal and OK
  // More explicit but unnecessary:
  s = static_cast<Shape*>(&c);
  // (Since upcasting is such a safe and common
  // operation, the cast becomes cluttering)
  Circle* cp = 0;
  Square* sp = 0;
  // Static Navigation of class hierarchies
  // requires extra type information:
  if(typeid(s) == typeid(cp)) // C++ RTTI
    cp = static_cast<Circle*>(s);
  if(typeid(s) == typeid(sp))
    sp = static_cast<Square*>(s);
  if(cp != 0)
    cout << "It's a circle!" << endl;
  if(sp != 0)
    cout << "It's a square!" << endl;
  // Static navigation is ONLY an efficiency hack;
  // dynamic_cast is always safer. However:
  // Other* op = static_cast<Other*>(s);
  // Conveniently gives an error message, while
  Other* op2 = (Other*)s;
  // does not
} ///:~

In this program, a new feature is used that is not fully described until Volume 2 of this book, where a chapter is given to the topic: C++’s run-time type information (RTTI) mechanism. RTTI allows you to discover type information that has been lost by upcasting. The dynamic_cast is actually one form of RTTI. Here, the typeid keyword (declared in the header file <typeinfo>) is used to detect the types of the pointers. You can see that the type of the upcast Shape pointer is successively compared to a Circle pointer and a Square pointer to see if there’s a match. There’s more to RTTI than typeid, and you can also imagine that it would be fairly easy to implement your own type information system using a virtual function.

A Circle object is created and the address is upcast to a Shape pointer; the second version of the expression shows how you can use static_cast to be more explicit about the upcast. However, since an upcast is always safe and it’s a common thing to do, I consider an explicit cast for upcasting to be cluttering and unnecessary.

RTTI is used to determine the type, and then static_cast is used to perform the downcast. But notice that in this design the process is effectively the same as using dynamic_cast, and the client programmer must do some testing to discover the cast that was actually successful. You’ll typically want a situation that’s more deterministic than in the example above before using static_cast rather than dynamic_cast (and, again, you want to carefully examine your design before using dynamic_cast).

If a class hierarchy has no virtual functions (which is a questionable design) or if you have other information that allows you to safely downcast, it’s a tiny bit faster to do the downcast statically than with dynamic_cast. In addition, static_cast won’t allow you to cast out of the hierarchy, as the traditional cast will, so it’s safer. However, statically navigating class hierarchies is always risky and you should use dynamic_cast unless you have a special situation.

Thinking in C++
Prev Contents / Index Next

 
 
   Reproduced courtesy of Bruce Eckel, MindView, Inc. Design by Interspire