Читаем Thinking In C++. Volume 2: Practical Programming полностью

As you saw in the earlier program that used the hierarchy of Security classes, dynamic_cast can detect both exact types and, in an inheritance hierarchy with multiple levels, intermediate types. Here is another example.

//: C08:IntermediateCast.cpp

#include

#include

using namespace std;

class B1 {

public:

  virtual ~B1() {}

};

class B2 {

public:

  virtual ~B2() {}

};

class MI : public B1, public B2 {};

class Mi2 : public MI {};

int main() {

  B2* b2 = new Mi2;

  Mi2* mi2 = dynamic_cast(b2);

  MI* mi = dynamic_cast(b2);

  B1* b1 = dynamic_cast(b2);

  assert(typeid(b2) != typeid(Mi2*));

  assert(typeid(b2) == typeid(B2*));

  delete b2;

} ///:~

This example has the extra complication of multiple inheritance (more on this later in this chapter). If you create an Mi2 and upcast it to the root (in this case, one of the two possible roots is chosen), the dynamic_cast back to either of the derived levels MI or Mi2 is successful.

You can even cast from one root to the other:

  B1* b1 = dynamic_cast(b2);

This is successful because B2 is actually pointing to an Mi2 object, which contains a subobject of type B1.

Casting to intermediate levels brings up an interesting difference between dynamic_cast and typeid. The typeid operator always produces a reference to a static typeinfo object that describes the dynamic type of the object. Thus, it doesn’t give you intermediate-level information. In the following expression (which is true), typeid doesn’t see b2 as a pointer to the derived type, like dynamic_cast does:

typeid(b2) != typeid(Mi2*)

The type of b2 is simply the exact type of the pointer:

typeid(b2) == typeid(B2*)

<p>void pointers</p>

RTTI only works for complete types, meaning that all class information must be available when typeid is used. In particular, it doesn’t work with void pointers:

//: C08:VoidRTTI.cpp

// RTTI & void pointers

//!#include

#include

using namespace std;

class Stimpy {

public:

  virtual void happy() {}

  virtual void joy() {}

  virtual ~Stimpy() {}

};

int main() {

  void* v = new Stimpy;

  // Error:

//!  Stimpy* s = dynamic_cast(v);

  // Error:

//!  cout << typeid(*v).name() << endl;

} ///:~

A void* truly means "no type information at all."[106] 

<p>Using RTTI with templates</p>

Class templates work well with RTTI, since all they do is generate classes. As usual, RTTI provides a convenient way to obtain the name of the class you’re in. The following example prints the order of constructor and destructor calls:

//: C08:ConstructorOrder.cpp

// Order of constructor calls

#include

#include

using namespace std;

template class Announce {

public:

  Announce() {

    cout << typeid(*this).name()

         << " constructor" << endl;

  }

  ~Announce() {

    cout << typeid(*this).name()

         << " destructor" << endl;

  }

};

class X : public Announce<0> {

  Announce<1> m1;

  Announce<2> m2;

public:

  X() { cout << "X::X()" << endl; }

  ~X() { cout << "X::~X()" << endl; }

};

int main() { X x; } ///:~

This template uses a constant int to differentiate one class from another, but type arguments would work as well. Inside both the constructor and destructor, RTTI information produces the name of the class to print. The class X uses both inheritance and composition to create a class that has an interesting order of constructor and destructor calls. The output is:

Announce<0> constructor

Announce<1> constructor

Announce<2> constructor

X::X()

X::~X()

Announce<2> destructor

Announce<1> destructor

Announce<0> destructor

<p>Multiple inheritance</p>

Of course, the RTTI mechanisms must work properly with all the complexities of multiple inheritance, including virtual base classes (discussed in depth in the next chapter—you may want to come back to this after reading Chapter 9):

//: C08:RTTIandMultipleInheritance.cpp

#include

#include

using namespace std;

class BB {

public:

  virtual void f() {}

  virtual ~BB() {}

};

class B1 : virtual public BB {};

class B2 : virtual public BB {};

class MI : public B1, public B2 {};

int main() {

  BB* bbp = new MI; // Upcast

  // Proper name detection:

  cout << typeid(*bbp).name() << endl;

  // Dynamic_cast works properly:

  MI* mip = dynamic_cast(bbp);

  // Can't force old-style cast:

//! MI* mip2 = (MI*)bbp; // Compile error

} ///:~

The typeid( ) operation properly detects the name of the actual object, even through the virtual base class pointer. The dynamic_cast also works correctly. But the compiler won’t even allow you to try to force a cast the old way:

MI* mip = (MI*)bbp; // Compile-time error

The compiler knows this is never the right thing to do, so it requires that you use a dynamic_cast.

<p>Sensible uses for RTTI</p>
Перейти на страницу:

Похожие книги

3ds Max 2008
3ds Max 2008

Одни уверены, что нет лучшего способа обучения 3ds Мах, чем прочитать хорошую книгу. Другие склоняются к тому, что эффективнее учиться у преподавателя, который показывает, что и как нужно делать. Данное издание объединяет оба подхода. Его цель – сделать освоение 3ds Мах 2008 максимально быстрым и результативным. Часто после изучения книги у читателя возникают вопросы, почему не получился тот или иной пример. Видеокурс – это гарантия, что такие вопросы не возникнут: ведь автор не только рассказывает, но и показывает, как нужно работать в 3ds Мах.В отличие от большинства интерактивных курсов, где работа в 3ds Мах иллюстрируется на кубиках-шариках, данный видеокурс полностью практический. Все приемы работы с инструментами 3ds Мах 2008 показаны на конкретных примерах, благодаря чему после просмотра курса читатель сможет самостоятельно выполнять даже сложные проекты.

Владимир Антонович Верстак , Владимир Верстак

Программирование, программы, базы данных / Программное обеспечение / Книги по IT