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

This example is much shorter, since most of the code in the original example was just the overhead for checking the casts. The target type of a dynamic_cast is placed in angle brackets, like the other new-style C++ casts (static_cast, and so on), and the object to cast appears as the operand. dynamic_cast requires that the types you use it with be polymorphic if you want safe downcasts.[105] This in turn requires that the class must have at least one virtual function. Fortunately, the Security base class has a virtual destructor, so we didn’t have to invent some extraneous function to get the job done. dynamic_cast does its work at runtime, of course, since it has to check the virtual function table of objects according to there dynamic type. This naturally implies that dynamic_cast tends to be more expensive than the other new-style casts.

You can also use dynamic_cast with references instead of pointers, but since there is no such thing as a null reference, you need another way to know if the cast fails. That "other way" is to catch a bad_cast exception, as follows:

  Metal m;

  Security& s = m;

  try {

    Investment& c = dynamic_cast(s);

    cout << "  it's an Investment\n";

  }

  catch (bad_cast&) {

    cout << "s is not an Investment type\n";

  }

The bad_cast class is defined in the header, and, like most of the standard library, is declared in the std namespace.

<p>The typeid operator</p>

The other way to get runtime information for an object is through the typeid operator. This operator returns an object of class type_info, which yields information about the type of object to which it was applied. If the type is polymorphic, it gives information about the most derived type that applies (the dynamic type); otherwise it yields static type information. One use of the typeid operator is to get the name of the dynamic type of an object as a const char*, as you can see in the following example.

//: C08:TypeInfo.cpp

// Illustrates the typeid operator

#include

#include

using namespace std;

struct PolyBase {virtual ~PolyBase(){}};

struct PolyDer : PolyBase {};

struct NonPolyBase {};

struct NonPolyDer : NonPolyBase {NonPolyDer(int){}};

int main() {

  // Test polymorphic Types

  const PolyDer pd;

  const PolyBase* ppb = &pd

  cout << typeid(ppb).name() << endl;

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

  cout << boolalpha << (typeid(*ppb) == typeid(pd))

    << endl;

  cout << (typeid(PolyDer) == typeid(const PolyDer))

    << endl;

  // Test non-polymorphic Types

  const NonPolyDer npd(1);

  const NonPolyBase* nppb = &npd

  cout << typeid(nppb).name() << endl;

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

  cout << (typeid(*nppb) == typeid(npd))

    << endl;

  // Test a built-in type

  int i;

  cout << typeid(i).name() << endl;

} ///:~

The output from this program is

struct PolyBase const *

struct PolyDer

true

true

struct NonPolyBase const *

struct NonPolyBase

false

int

The first output line just echoes the static type of ppb because it is a pointer. To get RTTI to kick in, you need to look at the object a pointer or reference is connected to, which is illustrated in the second line. Notice that RTTI ignores top-level const and volatile qualifiers. With non-polymorphic types, you just get the static type (the type of the pointer itself). As you can see, built-in types are also supported.

It turns out that you can’t store the result of a typeid operation in a type_info object, because there are no accessible constructors and assignment is disallowed; you must use it as we have shown. In addition, the actual string returned by type_info::name( ) is compiler dependent. Some compilers return "class C" instead of just "C", for instance, for a class named C. Applying typeid to an expression that dereferences a null pointer will cause a bad_typeid exception (also defined in ) to be thrown.

The following example shows that the class name that type_info::name( ) returns is fully qualified.

//: C08:RTTIandNesting.cpp

#include

#include

using namespace std;

class One {

  class Nested {};

  Nested* n;

public:

  One() : n(new Nested) {}

  ~One() { delete n; }

  Nested* nested() { return n; }

};

int main() {

  One o;

  cout << typeid(*o.nested()).name() << endl;

} ///:~

Since Nested is a member type of the One class, the result is One::Nested.

You can also ask a type_info object if it precedes another type_info object in the implementation-defined "collation sequence" (the native ordering rules for text), using before(type_info&), which returns true or false. When you say,.

if(typeid(me).before(typeid(you))) // ...

you’re asking if me occurs before you in the current collation sequence. This is useful should you use type_info objects as keys.

<p>Casting to intermediate levels</p>
Перейти на страницу:

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

3ds Max 2008
3ds Max 2008

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

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

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