Лекции.ИНФО


Эта программа генерирует такие результаты.



Объект o1 имеет тип class myclass<int>

Объект o2 имеет тип class myclass<int>

Объект o3 имеет тип class myclass<double>

Объекты o1 и o2 имеют одинаковый тип.

Объекты o1 и o3 имеют разные типы.

Как видите, несмотря на то, что два объекта являются экземплярами одного и того же шаблонного класса, если их параметризованные данные не совпадают, они не эквивалентны по типу. В этой программе объект o1 имеет тип myclass<int>, а объект o3 — тип myclass<double>. Таким образом, это объекты разного типа.

Рассмотрим еще один пример применения оператора typeid к шаблонным классам, а именно модифицированную версию программы определения геометрических фигур из предыдущего раздела. На этот раз класс figure мы сделали шаблонным.

// Шаблонная версия figure-иерархии.

#include <iostream>

#include <cstdlib>

using namespace std;

template <class T>

Class figure

{

 protected:

  T x, y;

 public:

  figure(T i, T j) {

   x = i;

   у = j;

  }

  virtual T area() = 0;

};

template <class T>

class triangle : public figure<T>

{

 public:

  triangle(T i, T j) : figure<T>(i, j) {}

  T area() {

   return x * 0.5 * y;

  }

};

template <class T>

class rectangle : public figure<T>

{

 public:

  rectangle(T i, T j) : figure<T>(i, j) {}

  T area() {

   return x * y;

  }

};

template <class T>

class circle : public figure<T>

{

 public:

  circle(T i, T j=0) : figure<T>(i, j) {}

  T area() {

   return 3.14 * x * x;

  }

};

// Фабрика объектов, генерируемых из класса figure.

figure<double> *generator()

{

 switch(rand() % 3 ) {

  case 0: return new circle<double> (10.0);

  case 1: return new triangle<double>(10.1, 5.3);

  case 2: return new rectangle<double> (4.3, 5.7);

 }

 return 0;

}

Int main()

{

 figure<double> *p;

 int i;

 int t=0, c=0, r=0;

 // генерируем и подсчитываем объекты

 for(i=0; i<10; i++) {

  p = generator();

  cout << "Объект имеет тип " << typeid(*p).name();

  cout << ". ";

  // учитываем объект

  if(typeid(*p) == typeid(triangle<double>)) t++;

  if(typeid(*p) == typeid(rectangle<double>)) r++;

  if(typeid(*p) == typeid(circle<double>)) c++;

  cout << "Площадь равна " << p->area() << endl;

 }

 cout << endl;

 cout << "Сгенерированы такие объекты:";

 cout << " треугольников: " << t << endl;

 cout << " прямоугольников: " << r << endl;

 cout << " кругов: " << с << endl;

 return 0;

}

Вот как выглядит возможный результат выполнения этой программы.

Объект имеет тип class rectangle<double>. Площадь равна 24.51

Объект имеет тип class rectangle<double>. Площадь равна 24.51

Объект имеет тип class triangle<double>. Площадь равна 26.765

Объект имеет тип class triangle<double>. Площадь равна 26.765

Объект имеет тип class rectangle<double>. Площадь равна 24.51

Объект имеет тип class triangle<double>. Площадь равна 26.765

Объект имеет тип class circle<double>. Площадь равна 314

Объект имеет тип class circle<double>. Площадь равна 314

Объект имеет тип class triangle<double>. Площадь равна 26.765

Объект имеет тип class rectangle<double>. Площадь равна 24.51

Сгенерированы такие объекты:

 треугольников: 4

 прямоугольников: 4

 кругов: 2

Динамическая идентификация типов используется не в каждой программе. Но при работе с полиморфными типами это средство позволяет узнать тип объекта, обрабатываемого в любой произвольный момент времени.

Операторы приведения типов

В C++ определено пять операторов приведения типов. Первый оператор (он описан выше в этой книге), применяемый в обычном (традиционном) стиле, был с самого начала встроен в C++. Остальные четыре (dynamic_cast, const_cast, reinterpret_cast и static_cast) были добавлены в язык всего несколько лет назад. Эти операторы предоставляют дополнительные "рычаги управления" характером выполнения операций приведения типа. Рассмотрим каждый из них в отдельности.

Оператор dynamic_cast

Оператор dynamic_cast выполняет операцию приведения полиморфных типов во время выполнения программы.

Возможно, самым важным из новых операторов является оператор динамического приведения типов dynamic_cast. Во время выполнения программы он проверяет обоснованность предлагаемой операции. Если в момент его вызова заданная операция оказывается недопустимой, приведение типов не производится. Общий формат применения оператора dynamic_cast таков.

dynamic_cast<type> (expr)

Здесь элемент type означает новый тип, который является целью выполнения этой операции, а элемент expr— выражение, приводимое к этому новому типу. Тип type должен быть представлен указателем или ссылкой, а выражение expr должно приводиться к указателю или ссылке. Таким образом, оператор dynamic_cast можно использовать для преобразования указателя одного типа в указатель другого или ссылки одного типа в ссылку другого.

Этот оператор в основном используется для динамического выполнения операций приведения типа среди полиморфных типов. Например, если даны полиморфные классы В и D, причем класс D выведен из класса В, то с помощью оператора dynamic_cast всегда можно преобразовать указатель D* в указатель В*, поскольку указатель на базовый класс всегда можно использовать для указания на объект класса, выведенного из базового. Однако оператор dynamic_cast может преобразовать указатель В* в указатель D* только в том случае, если адресуемым объектом действительно является объект класса D. И, вообще, оператор dynamic_cast будет успешно выполнен только при условии, если разрешено полиморфное приведение типов, т.е. если указатель (или ссылка), приводимый к новому типу, может указывать (или ссылаться) на объект этого нового типа или объект, выведенный из него. В противном случае, т.е. если заданную операцию приведения типов выполнить нельзя, результат действия оператора dynamic_cast оценивается как нулевой, если в этой операции участвуют указатели. (Если же попытка выполнить эту операцию оказалась неудачной при участии в ней ссылок, генерируется исключение типа bad_cast.)

Рассмотрим простой пример. Предположим, что класс Base — полиморфный, а класс Derived выведен из класса Base.

Base *bp, b_ob;

Derived *dp, d_ob;

bp = &d_ob; // Указатель на базовый класс указывает на объект класса Derived.

dp = dynamic_cast<Derived *> (bp); // Приведение к указателю на производный класс разрешено.

if(dp) cout << "Приведение типа прошло успешно!";

Здесь приведение указателя bp (на базовый класс) к указателю dp (на производный класс) успешно выполняется, поскольку bp действительно указывает на объект класса Derived. Поэтому при выполнении этого фрагмента кода будет выведено сообщение Приведение типа прошло успешно!. Но в следующем фрагменте кода попытка совершить операцию приведения типа будет неудачной, поскольку bp в действительности указывает на объект класса Base, и неправомерно приводить указатель на базовый класс к типу указателя на производный, если адресуемый им объект не является на самом деле объектом производного класса.

bp = &b_ob; /* Указатель на базовый класс ссылается на объект класса Base. */

dp = dynamic_cast<Derived *> (bp); // ошибка!

if(!dp) cout << "Приведение типа выполнить не удалось";

Поскольку попытка выполнить операцию приведения типа оказалась неудачной, при выполнении этого фрагмента кода будет выведено сообщение Приведение типа выполнить не удалось.

В следующей программе демонстрируются различные ситуации применения оператора dynamic_cast.

// Использование оператора dynamic_cast.

#include <iostream>

using namespace std;

class Base {

 public:

  virtual void f() { cout << "В классе Base."; }

  // . . .

};

class Derived : public Base {

 public:

  void f() { cout << "В классе Derived."; }

};

Int main()

{

 Base *bp, b_ob;

 Derived *dp, d_ob;

 dp = dynamic_cast<Derived *> (&d_ob);

 if(dp) {

  cout << "Приведение типов " <<"(из Derived * в Derived *) реализовано.";

  dp->f();

 }

 else cout <<"Ошибка";

 cout << endl;

 bp = dynamic_cast<Base *> (&d_ob);

 if(bp) {

  cout << "Приведение типов " <<"(из Derived * в Base *) реализовано.";

  bp->f();

 }

 else cout << "Ошибка";

 cout << endl;

 bp = dynamic_cast<Base *> (&b_ob);

 if(bp) {

  cout << "Приведение типов " <<"(из Base * в Base *) реализовано.";

  bp->f();

 }

 else cout << "Ошибка";

 cout << endl;

 dp = dynamic_cast<Derived *> (&b_ob);

 if(dp) cout <<"Ошибка";

 else

  cout <<"Приведение типов " <<"(из Base * в Derived *) не реализовано.";

 cout << endl;

 bp = &d_ob; // bp указывает на объект класса Derived

 dp = dynamic_cast<Derived *> (bp);

 if(dp) {

  cout << "Приведение bp к типу Derived *" << "реализовано, поскольку bp действительно" << "указывает на объект класса Derived.";

  dp->f();

 }

 else cout << "Ошибка";

 cout << endl;

 bp = &b_ob; // bр указывает на объект класса Base

 dp = dynamic_cast<Derived *> (bp);

 if(dp) cout << "Ошибка";

 else {

  cout <<"Теперь приведение bp к типу Derived *" <<"не реализовано, поскольку bp" <<"в действительности указывает на объект" <<"класса Base.";

 }

 cout << endl;

 dp = &d_ob; // dp указывает на объект класса Derived

 bp = dynamic_cast<Base *> (dp);

 if(bp) {

  cout <<"Приведение dp к типу Base * реализовано.";

  bp->f();

 }

 else cout <<"Ошибка";

 return 0;

}









Читайте также:

Последнее изменение этой страницы: 2016-03-17; Просмотров: 83;


lektsia.info 2017 год. Все права принадлежат их авторам! Главная