Читаем 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>
Перейти на страницу:

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

1С: Бухгалтерия 8 с нуля
1С: Бухгалтерия 8 с нуля

Книга содержит полное описание приемов и методов работы с программой 1С:Бухгалтерия 8. Рассматривается автоматизация всех основных участков бухгалтерии: учет наличных и безналичных денежных средств, основных средств и НМА, прихода и расхода товарно-материальных ценностей, зарплаты, производства. Описано, как вводить исходные данные, заполнять справочники и каталоги, работать с первичными документами, проводить их по учету, формировать разнообразные отчеты, выводить данные на печать, настраивать программу и использовать ее сервисные функции. Каждый урок содержит подробное описание рассматриваемой темы с детальным разбором и иллюстрированием всех этапов.Для широкого круга пользователей.

Алексей Анатольевич Гладкий

Программирование, программы, базы данных / Программное обеспечение / Бухучет и аудит / Финансы и бизнес / Книги по IT / Словари и Энциклопедии
1С: Управление торговлей 8.2
1С: Управление торговлей 8.2

Современные торговые предприятия предлагают своим клиентам широчайший ассортимент товаров, который исчисляется тысячами и десятками тысяч наименований. Причем многие позиции могут реализовываться на разных условиях: предоплата, отсрочка платежи, скидка, наценка, объем партии, и т.д. Клиенты зачастую делятся на категории – VIP-клиент, обычный клиент, постоянный клиент, мелкооптовый клиент, и т.д. Товарные позиции могут комплектоваться и разукомплектовываться, многие товары подлежат обязательной сертификации и гигиеническим исследованиям, некондиционные позиции необходимо списывать, на складах периодически должна проводиться инвентаризация, каждая компания должна иметь свою маркетинговую политику и т.д., вообщем – современное торговое предприятие представляет живой организм, находящийся в постоянном движении.Очевидно, что вся эта кипучая деятельность требует автоматизации. Для решения этой задачи существуют специальные программные средства, и в этой книге мы познакомим вам с самым популярным продуктом, предназначенным для автоматизации деятельности торгового предприятия – «1С Управление торговлей», которое реализовано на новейшей технологической платформе версии 1С 8.2.

Алексей Анатольевич Гладкий

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