Using RTTI
To customize the menu dynamically, the file manager has to probe each files dynamic type.
Operator typeid
Operator typeid retrieves the runtime type information associated with a certain object. typeid takes an object or a type name as its argument. Thus, to determine if the dynamic type of x is Y, check whether the expression typeid(x) == typeid(Y) is true:
#include <typeinfo> // needed for typeid
void menu::build(const File * pfile)
{
if (typeid(*pfile)==typeid(TextFile))
{
add_option("edit");
}
else if (typeid(*pfile)==typeid(MediaFile))
{
add_option("play");
}
}
| TIP: Certain compilers, such as Visual C++, disable RTTI by default to eliminate performance overhead. If your program does use RTTI, remember to enable RTTI before compilation.
|
The use of typeid might introduce maintenance problems in the future. Suppose you decide to extend the class hierarchy and derive another class from MediaFile called LocalizedMedia that represents a media file with subtitles in various languages. Yet in essence, a LocalizedMedia file is a MediaFile. Therefore, the file manager should display the "play" option when the user right clicks on a LocalizedMedia file. Unfortunately, the build() member function will fail to do so because you didnt include a test for this particular file type. To fix this, you can patch it like this:
void menu::build(const File * pfile)
{
//..
else if (typeid(*pfile)==typeid(LocalizedMedia))
{
add_option("play");
}
}
Alas, this function will have to be patched every time you add a new class. Clearly, this isn't an ideal solution.
Operator dynamic_cast
You need a way to determine whether a certain object is a MediaFile or any class derived from it. This is exactly what operator dynamic_cast does. dynamic_cast takes two arguments: a type name and a pointer or a reference to a polymorphic object. It attempts to cast at runtime the object to the target type and returns the result. Put differently, if the function succeeds in casting *pfile to MediaFile dynamically, then pfile's dynamic type is MediaFile or a class derived from it. Otherwise, pfile is a different beast:
void menu::build(const File * pfile)
{
if (dynamic_cast <MediaFile *> (pfile))
{
// pfile is MediaFile or LocalizedMedia
add_option("play");
}
else if (dynamic_cast <TextFile*> (pfile))
{
// pfile is a TextFile or a class derived from it
add_option("edit");
}
}
Moderation is Advised
Although the use of dynamic_cast solves this problem neatly, it exacts a toll. As opposed to typeid, a dynamic cast isn't a constant time operation. In order to determine whether the cast can be performed, dynamic_cast must traverse the derivation lattice of its argument at runtime. Therefore, use dynamic_cast judiciously.