- Главная
- Информатика
- Динамическая идентификация типа ООП
Содержание
- 2. ООП Для идентификации типа объекта используется оператор typeid, определенный в заголовке #include . Его формат: typeid
- 3. ООП #include #include using namespace std; class myclass1 { /* ... */ }; class myclass2 {
- 4. ООП #include #include using namespace std; class Mammal {public: //класс Mammal – полиморфный virtual bool lays_eggs()
- 5. ООП Динамическая идентификация типа Если оператор typeid применяется к указателю на объект полиморфного базового класса, тип
- 6. ООП #include using namespace std; class Mammal { public: virtual bool lays_eggs() {return false;} // ...
- 7. ООП #include using namespace std; template class myclass { T a; public: myclass(T i) { a
- 8. ООП В языке С++ существуют пять операторов приведения типов. Первый оператор является вполне традиционным и унаследован
- 9. ООП Оператор dynamic_cast Операторы приведения типа Оператор dynamic_cast осуществляет динамическое приведение типа с последующей проверкой корректности
- 10. ООП Оператор dynamic_cast Операторы приведения типа Пример. Класс Base является полиморфным, a Derived – производным от
- 11. ООП Оператор dynamic_cast Операторы приведения типа Пример. Программа демонстрирует разные ситуации применения оператора dynamic_cast #include using
- 12. ООП Оператор dynamic_cast Операторы приведения типа Пример. Программа демонстрирует разные ситуации применения оператора dynamic_cast Результаты работы
- 13. ООП Замена оператора typeid оператором dynamic_cast Операторы приведения типа Иногда оператор dynamic_cast можно использовать вместо оператора
- 14. ООП Оператор dynamic_cast Операторы приведения типа Пример. В этой программе одна и та же операция выполняется
- 15. ООП Применение dynamic_cast к шаблонным классам Операторы приведения типа #include using namespace std; template class Num
- 16. ООП Оператор const_cast Операторы приведения типа Оператор const_cast используется для явного замещения модификаторов const и/или volatile.
- 17. ООП Оператор static_cast Операторы приведения типа Оператор static_cast выполняет неполиморфное приведение. Его можно применять для любого
- 19. Скачать презентацию
ООП
Для идентификации типа объекта используется оператор typeid, определенный в заголовке
ООП
Для идентификации типа объекта используется оператор typeid, определенный в заголовке
Его формат: typeid (object)
Здесь, параметр object является объектом, тип которого мы хотим идентифицировать. Он может иметь любой тип, в том числе встроенный или определенный пользователем.
Оператор typeid возвращает ссылку на объект типа type_infо, описывающий тип объекта. Стандарт "ISO/IEC 14882:2003(Е), Programming languages - С++" определяет класс type_infо так:
Динамическая идентификация типа
class type_info
{
public:
virtual ?type_info();
bool operator == (const type_info& rhs) const;
bool operator != (const type_info& rhs) const;
bool before(const type_info& rhs) const;
const char* name() const;
private:
type_info(const type_info& rhs);
type_info& operator=(const type_info& rhs);
};
Как видно, объекты типа type_info невозможно ни создать, ни скопировать – конструкторы и операция присваивания закрыты.
Объекты типа type_info можно сравнивать между собой, используя перегружен-ные операторы "==" и "!=", – и это основные операции, которые используются сов-местно с оператором typeid(); функция name() возвращает указатель на имя заданного типа.
Метод before() позволяет сортировать информацию о типе type_info. Нет никакой связи между отношениями упорядочения, определяемыми before(), и отношениями наследования. Если вызывающий объект предшествует объекту, использованному как параметр, функция before() возвращает значение true.
ООП
#include
#include
using namespace std;
class myclass1 { /* ... */
ООП
#include
#include
using namespace std;
class myclass1 { /* ... */
class myclass2 { /* … */ };
int main()
{ int i, j; float f; char *p;
myclass1 ob1; myclass2 ob2;
cout <<"Тип объекта i: "<< typeid(i).name();
cout << endl;
cout <<"Тип объекта f: "<< typeid(f).name();
cout << endl;
cout <<"Тип объекта p: "<< typeid(p).name();
cout << endl;
cout <<"Тип объекта ob1: "<< typeid(ob1).name();
cout << endl;
cout <<"Тип объекта ob2: "<< typeid(ob2).name();
cout << "\n\n";
if(typeid(i) == typeid(j))
cout <<"Типы объектов i и j совпадают\n";
if(typeid(i) != typeid(f))
cout <<"Типы объектов i и f не совпадают\n";
if(typeid(ob1) != typeid(ob2))
cout <<"ob1 и ob2 имеют разные типы\n";
return 0; }
Динамическая идентификация типа
Результаты работы этой программы приведены ниже.
Тип объекта i: int
Тип объекта f: float
Тип объекта р: char *
Тип объекта оb1: class myclass1
Тип объекта ob2: class myclass2
Типы объектов i и j совпадают
Типы объектов i и f не совпадают
Объекты оb1 и оb2 имеют разные типы
Пример использования typeid
ООП
#include
#include
using namespace std;
class Mammal {public: //класс Mammal –
ООП
#include
#include
using namespace std;
class Mammal {public: //класс Mammal –
virtual bool lays_eggs() { return false; }
// ... };
class Cat: public Mammal { public: /* ... */ };
class Platypus: public Mammal { public:
bool lays_eggs() { return true; } /* ... */ };
int main()
{ Mammal *p, AnyMammal;
Cat cat;
Platypus platypus;
p = &AnyMammal;
cout << "Указатель p ссылается на объект типа ";
cout << typeid(*p).name() << endl;
p = &cat;
cout << "Указатель p ссылается на объект типа ";
cout << typeid(*p).name() << endl;
p = &platypus;
cout << "Указатель p ссылается на объект типа ";
cout << typeid(*p).name() << endl;
return 0;
}
Динамическая идентификация типа
Самое важное свойство оператора typeid проявляется, когда он применяется к указателю на объект базового класса. В этом случае он автоматически возвращает тип реального объекта, на который ссылается указатель, причем этот объект может быть экземпляром как базового, так и производного классов. (Напомним, что указатель на объект базового класса может ссылаться на объекты производного класса.)
Т.о, в ходе выполнения программы, используя оператор typeid, можно идентифицировать тип объекта, на который ссылается указатель базового класса.
Результаты работы этой программы приведены ниже.
Указатель р ссылается на объект типа class Mammal,
Указатель р ссылается на объект типа class Cat,
Указатель р ссылается на объект типа class Platypus.
Пример применения typeid к иерархии полиморфных классов
ООП
Динамическая идентификация типа
Если оператор typeid применяется к указателю на объект полиморфного
ООП
Динамическая идентификация типа
Если оператор typeid применяется к указателю на объект полиморфного
В любом случае, если оператор typeid применяется к указателю на объект неполиморфного базового класса, возвращается базовый тип указателя. Иначе говоря, невозможно определить, на объект какого типа ссылается этот указатель на самом деле.
Закомментируем, например, ключевое слово virtual, стоящее перед именем функции lays_eggs() в классе Mammal, перекомпилируем программу и запустим ее вновь:
Указатель р ссылается на объект типа class Mammal
Указатель р ссылается на объект типа class Mammal
Указатель р ссылается на объект типа class Mammal
Класс Mammal больше не является полиморфным, и каждый объект теперь имеет тип Mammal, т.е. тип указателя р.
Поскольку оператор typeid обычно применяется к разыменованным указателям, следует предусмотреть обработку исключительной ситуации bad_typeid, которая может возникнуть, если указатель будет нулевым.
Оператор typeid имеет вторую форму, получающую в качестве аргумента имя типа.
Ее общий вид приведен ниже:
typeid(<имя_типа>)
Например, следующий оператор совершенно правилен: cout << typeid(int).name();
Данная форма оператора typeid позволяет получить объект класса type_info, описывающий заданный тип. Благодаря этому ее можно применять в операторах сравнения типов.
Например: void WhatMammal(Mammal &ob)
{ cout << "Параметр ob ссылается на объект типа ";
cout << typeid(ob).name() << endl;
if(typeid(ob) == typeid(Cat))
cout << "Кошки боятся воды.\n";
}
Разбор примера применения typeid к иерархии полиморфных классов
ООП
#include
using namespace std;
class Mammal { public:
virtual bool lays_eggs()
ООП
#include
using namespace std;
class Mammal { public:
virtual bool lays_eggs()
// ... };
class Cat: public Mammal { public: /* … /* };
class Platypus: public Mammal { public:
bool lays_eggs() { return true; } /* ... */ };
class Dog: public Mammal { public: /* … /* };
// Фабрика объектов, производных от класса Mammal
Mammal *factory()
{ switch(rand() % 3 ) { case 0: return new Dog;
сase 1: return new Cat; case 2: return new Platypus; }
return 0; }
int main()
{ Mammal *ptr; // Указатель на базовый класс
int i; int c=0, d=0, p=0;
// Создаем и подсчитываем объекты
for(i=0; i<10; i++) // Создаем и подсчитываем объекты
{ ptr = factory(); // Создаем объект
cout<<"Тип объекта: "<< typeid(*ptr).name();
cout<< endl;
// Подсчёт
if(typeid(*ptr) == typeid(Dog)) d++;
if(typeid(*ptr) == typeid(Cat)) c++;
if(typeid(*ptr) == typeid(Platypus)) p++; }
cout << endl;
cout << " Созданные животные:\n";
cout << " Собаки: " << d << endl;
cout << " Кошки: " << c << endl;
cout << " Утконосы: " << p << endl;
return 0;
}
Динамическая идентификация типа
Функция factory() создает объекты различных классов, производных от класса Mammal. (Функции, создающие объекты, иногда называются фабриками объектов (object factory)). Конкретный тип создаваемого объекта задается функцией rand(), которая представляет собой генератор случайных чисел в языке С++. Т.е., тип создаваемого объекта заранее не известен. Программа создаст 10 объектов и подсчитывает количество объектов каждого типа. Поскольку все объекты генерируются функцией factory(), для идентификации фактического типа объекта используется оператор typeid.
Результаты работы этой программы приведены ниже.
Тип объекта: class Platypus
Тип объекта: class Platypus
Тип объекта: class Cat
Тип объекта: class Cat
Тип объекта: class Platypus
Тип объекта: class Cat
Тип объекта: class Dog
Тип объекта: class Dos
Тип объекта: class Cat
Тип объекта: class Platypus
Созданные животные:
Собаки: 2
Кошки: 4
Утконосы: 4
Применение динамической идентификации типа
ООП
#include
using namespace std;
template class myclass {
T
ООП
#include
using namespace std;
template
T
public:
myclass(T i) { a = i; } // ...
};
int main()
{ myclass
myclass
cout << "Тип объекта o1: ";
cout << typeid(o1).name() << endl;
cout << "Тип объекта o2: ";
cout << typeid(o2).name() << endl;
cout << "Тип объекта o3: ";
cout << typeid(o3).name() << endl;
cout << endl;
if(typeid(o1) == typeid(o2))
cout << "Объекты o1 и o2 имеют одинаковый тип\n";
if(typeid(o1) == typeid(o3))
cout << "Ошибка\n";
else
cout << "Объекты o1 и o3 имеют разные типы\n";
return 0;
}
Динамическая идентификация типа
Тип объекта, являющегося объектом шаблонного класса, определяется, в частности, тем, какие данные используются в качестве обобщенных при создании конкретного объекта. Если при создании двух экземпляров шаблонного класса используются данные разных типов, считается, что эти объекты имеют разные типы. Применение оператора typeid к шаблонным классам Динамическая идентификация типов применяется не во всех программах. Однако при работе с полиморфными типами механизм RTTI позволяет распознавать типы объектов в любых ситуациях.
Результаты работы этой программы приведены ниже.
Тип объекта o1: class myclass
Тип объекта o2: class myclass
Тип объекта o3: class myclass
Объекты ol и о2 имеют одинаковый типы Объекты ol и оЗ имеют разные типы
Как видим, хотя два объекта представляют собой экземпляры одного и того же шаблонного класса, если параметры не совпадают, их типы считаются разными. В данной программе объект o1 имеет тип myclass
ООП
В языке С++ существуют пять операторов приведения типов. Первый оператор
ООП
В языке С++ существуют пять операторов приведения типов. Первый оператор
Операция приведения типов в стиле С:
Она может записываться в двух формах:
тип (выражение)
(тип) выражение
Результатом операции является значение заданного типа, например:
int а = 2;
float b = 6.8;
printf ("%lf %d", double (a), (int) b);
Величина а преобразуется к типу double, а переменная b – к типу int с отсечением дробной части, в обоих случаях внутренняя форма представления результата операции преобразования иная, чем форма исходного значения.
Остальные четыре оператора приведения типов были добавлены впоследствии, в С++.
К ним относятся операторы: dynamic_cast,
const_cast,
reinterpret_cast,
static_cast.
Эти операторы позволяют полнее контролировать процессы приведения типов.
Операторы приведения типа
ООП
Оператор dynamic_cast
Операторы приведения типа
Оператор dynamic_cast осуществляет динамическое приведение типа с
ООП
Оператор dynamic_cast
Операторы приведения типа
Оператор dynamic_cast осуществляет динамическое приведение типа с
Общий вид оператора dynamic_cast:
dynamic_cast
Здесь - параметр target_type задает результирующий тип,
- параметр ехрr – выражение, которое приводится к новому типу.
Результирующий тип должен быть указательным или ссылочным, а приводимое выражение – вычислять указатель или ссылку. Таким образом, оператор dynamic_cast можно применять для приведения типов указателей или ссылок.
Оператор dynamic_cast предназначен для приведения полиморфных типов. Dynamic_cast применяется для приведения типов в иерархической структуре наследования; он может приводить базовый тип к производному, производный к базовому или один производный тип – к другому производному типу. Допустим, даны два полиморфных класса B и D, причем класс D является производным от класса B. Тогда оператор dynamic_cast может привести указатель типа D* к типу B*. Это возможно благодаря тому, что указатель на объект базового класса может ссылаться на объект производного класса. Однако обратное динамическое приведение указателя типа B* к типу D* возможно лишь в том случае, если указатель действительно ссылается на объект класса D.
Оператор dynamic_cast достигает цели, если указатель или ссылка, подлежавшие приведению, ссылаются на объект результирующего класса или объект класса, производного от результирующего. В противном случае приведение типов считается неудавшимся. В случае неудачи оператор dynamic_cast, примененный к указателям, возвращает нулевой указатель. Если оператор dynamic_cast применяется к ссылкам, в случае ошибки генерируется исключительная ситуация bad_cast.
ООП
Оператор dynamic_cast
Операторы приведения типа
Пример.
Класс Base является полиморфным, a Derived
ООП
Оператор dynamic_cast
Операторы приведения типа
Пример.
Класс Base является полиморфным, a Derived
Base *bp, b_ob;
Derived *dp, d_ob;
bp = &d_ob; // указатель базового типа ссылается на объект производного класса
dp=dynamic_cast
if(dp) cout << "Приведение выполнено успешно";
Приведение указателя bр, имеющего базовый тип, к указателю dp, имеющему производный тип, выполняется успешно, поскольку указатель bp на самом деле ссылается на объект класса Derived. Т.о, этот фрагмент выводит на экран сообщение "Приведение выполнено успешно".
Однако в следующем фрагменте приведение не выполняется, потому что указатель bр ссылается на объект класса Base, а приведение базового объекта к производному невозможно:
bp = &b_ob; // указатель базового типа ссылается на объект класса Base
dp = dynamic_cast
if(!dp) cout << "Приведение не выполняется";
Поскольку приведение невозможно, фрагмент выводит на экран сообщение "Приведение не выполняется".
ООП
Оператор dynamic_cast
Операторы приведения типа
Пример. Программа демонстрирует разные ситуации применения оператора
ООП
Оператор dynamic_cast
Операторы приведения типа
Пример. Программа демонстрирует разные ситуации применения оператора
#include if(dp) // продолжение
using namespace std;
class Base { public:
virtual void f() {cout<<"Внутри класса Base\n";} /* ... */ };
class Derived : public Base { public:
void f() {cout<< Внутри класса Derived \n"; } };
int main()
{ Base *bp, b_ob; Derived *dp, d_ob;
dp = dynamic_cast
if(dp) {
cout << "Приведение типа Derived * к типу Derived * выполнено успешно\n";
dp->f(); } else cout << "Ошибка\n";
cout << endl;
bp = dynamic_cast
if(bp) { cout << "Приведение типа Derived * к типу Base * выполнено успешно\n";
bp->f(); } else cout << "Ошибка\n";
cout << endl;
bp = dynamic_cast
if(bp) { сout << "Приведение типа Base * к типу Base * выполнено успешно\n";
bp->f(); } else cout << "Ошибка\n";
cout << endl;
dp = dynamic_cast
// см. продолжение
cout << "Ошибка\n"; else
cout <<"Приведение типа Base * к типу Derived * невозможно\n";
сout << endl;
bp = &d_ob; // Указатель bp ссылается на объект
класса Derived
dp = dynamic_cast
if(dp) {
сout<<"Приведение указателя bp к типу Derived * выполнено успешно поскольку bp действительно ссылается на объект класса Derived\n";
dp->f(); } else cout << "Ошибка\n";
cout << endl;
bp=&b_ob;//Указатель bp ссылается на объект класса Base
dp = dynamic_cast
if(dp) cout << "Error"; else
{cout<<"Теперь приведение указателя bp к типу Derived * невозможно, так как указатель bp на самом деле ссылается на объект класса Base\n"; }
cout << endl;
dp = &d_ob; // Указатель dp ссылается на объект класса Derived
bp = dynamic_cast
if(bp) {
cout<<"Приведение указателя dp к типу Base * выполнено успешно\n"; bp->f(); }
else cout << "Error\n";
return 0;
}
ООП
Оператор dynamic_cast
Операторы приведения типа
Пример. Программа демонстрирует разные ситуации применения оператора
ООП
Оператор dynamic_cast
Операторы приведения типа
Пример. Программа демонстрирует разные ситуации применения оператора
Результаты работы этой программы приведены ниже:
Приведение типа Derived * к типу Derived * выполнено успешно.
Внутри класса Derived
Приведение типа Derived * к типу Base * выполнено успешно.
Внутри класса Derived
Приведение типа Base * к типу Base * выполнено успешно.
Внутри класса Base
Приведение типа Base * к типу Derived * невозможно
Приведение указателя bp к классу Derived * выполнено успешно, поскольку указатель bp
действительно ссылается на объект класса Derived
Внутри класса Derived
Теперь приведение указателя bp к типу Derived невозможно, так как
указатель bp на самом деле ссылается на объект класса Base.
Приведение указателя dp к классу Base * выполнено успешно.
Внутри класса Derived
ООП
Замена оператора typeid оператором dynamic_cast
Операторы приведения типа
Иногда оператор dynamic_cast можно
ООП
Замена оператора typeid оператором dynamic_cast
Операторы приведения типа
Иногда оператор dynamic_cast можно
Допустим, что класс Base является полиморфным, a Derived ‒ производным от него. В следующем фрагменте указателю dp присваивается адрес объекта, на который ссылается указатель bр, только если этот объект действительно является экземпляром класса Derived:
Base *bp;
Derived *dp;
// ...
if(typeid(*bp) == typeid(Derived)) dp = (Derived *) bp;
В этом фрагменте используется традиционный оператор приведения. Это вполне безопасно, поскольку оператор if проверяет легальность приведения с помощью оператора typeid. Однако в этой ситуации лучше заменить оператор typeid и условный оператор if оператором dynamic_cast:
dp = dynamic_cast
Поскольку оператор dynamic_cast выполняется успешно, только если приводимый объект является либо экземпляром результирующего класса, либо объектом класса, производного от результирующего, указатель dp содержит либо нулевой указатель, либо адрес объектов типа Derived.
Поскольку оператор dynamic_cast выполняется успешно, только если приведение является легальным, его применение иногда упрощает ситуацию.
ООП
Оператор dynamic_cast
Операторы приведения типа
Пример. В этой программе одна и та
ООП
Оператор dynamic_cast
Операторы приведения типа
Пример. В этой программе одна и та
#include // продолжение
#include
using namespace std;
class Base { public: virtual void f() {} };
class Derived : public Base { public:
void derivedOnly()
{ cout << "Объект класса Derived \n"; } };
int main()
{ Base *bp, b_ob;
Derived *dp, d_ob;
// ************************************
// Применение оператора typeid
// ************************************
bp = &b_ob;
if(typeid(*bp) == typeid(Derived))
{ dp = (Derived *) bp; dp->derivedOnly(); }
else cout << "Приведение типа Base к типу
Derived невозможно\n";
bp = &d_ob;
if(typeid(*bp) == typeid(Derived))
{ dp = (Derived *) bp; dp->derivedOnly(); }
else
cout << "Ошибка, приведение невозможно!\n";
// см. продолжение
// ************************************
// Применение оператора dynamic_cast
// ************************************
bp = &b_ob;
dp = dynamic_cast
if(dp) dp->derivedOnly();
else cout << "Приведение типа Base к типу
Derived невозможно\n";
bp = &d_ob;
dp = dynamic_cast
if(dp) dp->derivedOnly();
else
cout<<"Ошибка, приведение невозможно!\n";
return 0;
}
Как видим, оператор dynamic_cast упрощает логику приведения указателя базового типа к указателю производного типа.
Приведение типа Base к типу Derived невозможно.
Объект класса Derived.
Приведение типа Base к типу Derived невозможно.
Объект класса Derived.
ООП
Применение dynamic_cast к шаблонным классам
Операторы приведения типа
#include
using namespace std;
template
ООП
Применение dynamic_cast к шаблонным классам
Операторы приведения типа
#include
using namespace std;
template
public: Num(T x) { val = x; }
virtual T getval() { return val; } /* ... */ };
template
{ public: SqrNum(T x) : Num
T getval() { return val * val; } };
int main()
{ Num
SqrNum
Num
bp = dynamic_cast
if(bp) { cout << "Приведение типа SqrNum
типу Num
cout<<"Значение равно "<
dp = dynamic_cast
if(dp) cout << "Ошибка\n"; else { cout<<"Приве-дение типа Num
cout<<"Приведение указателя на объект базового \n"; cout<<" класса к указателю на объект производного типа невозможно\n"; } cout << endl;
bp = dynamic_cast
if(bp) cout << "Ошибка\n"; else cout<<"Приведе-ние типа Num
return 0;
}
Результаты работы этой программы:
Приведение типа SqrNum
Значение равно 9
Приведение типа Num
Приведение указателя на объект базового класса к указателю на объект производного класса невозможно.
Приведение типа Num
Это два разных типа.
Основной смысл этого фрагмента заключается в том, что с помощью оператора dynamic_cast нельзя привести указатель на объект одной шаблонной специализации к указателю на экземпляр другой шаблонной специализации.
Напоминание: точный тип объекта шаблонного класса определяется типом данных, которые используются при его создании. Таким образом, типы Num
ООП
Оператор const_cast
Операторы приведения типа
Оператор const_cast используется для явного замещения модификаторов
ООП
Оператор const_cast
Операторы приведения типа
Оператор const_cast используется для явного замещения модификаторов
Общий вид оператора const_cast:
const_cast
Здесь - параметр type задает результирующий тип приведения,
- параметр ехрr – выражение, которое приводится к новому типу.
#include Эта программа выводит на экран следующие результаты:
using namespace std;
void sqrval(const int *val) { int *p;
// Удаление модификатора const
p = const_cast
*p = *val * *val; }
int main()
{ int x = 10;
cout<<"Значение x перед вызовом: "<
cout<<"Значение х после вызова: "<
}
Значение х перед вызовом: 10
Значение х после вызова: 100
Как видим, функция sqrval() изменяет значение переменной х, даже если ее параметр является константным указателем.
Применение оператора const_cast для удаления атрибута const небезопасно. Его следует использовать осторожно.
Атрибут const можно удалить только с помощью оператора const_cast.
Операторы dynamic_cast, static_cast и reinterpret_cast на атрибут const не влияют.
ООП
Оператор static_cast
Операторы приведения типа
Оператор static_cast выполняет неполиморфное приведение. Его можно
ООП
Оператор static_cast
Операторы приведения типа
Оператор static_cast выполняет неполиморфное приведение. Его можно
Оператор static_cast имеет следующий вид:
static_cast
Здесь - параметр type задает результирующий тип приведения,
- параметр ехрr – выражение, которое приводится к новому типу.
Оператор static_cast, по существу, заменяет исходный оператор приведения. Просто он выполняет неполиморфное приведение.
Например, следующая программа приводит переменную типа int к типу double:
#include
using namespace std;
int main()
{
int i;
for(i=0; i<10; i++)
cout << static_cast
return 0;
}