- •Объектно-ориентированное программирование:
- •Содержание
- •Новый тип данных – класс
- •1.2. Доступность компонентов класса
- •1.3. Конструктор и деструктор
- •1.4.4. Статические члены класса
- •1.4. Компоненты-данные и компоненты-функции
- •1.4.1. Данные – члены класса
- •1.4.2. Функции – члены класса
- •1.4.3. Константные компоненты-функции
- •Указатели на компоненты класса
- •1.5.1. Указатели на компоненты-данные
- •1.5.2.Указатели на компоненты-функции
- •1.6. Указатель this
- •1.7. Друзья классов
- •7.1. Дружественная функция
- •1.7.2. Дружественный класс
- •1.8. Определение классов и методов классов
- •2. Наследование
- •2.1. Определение производного класса
- •2.2. Конструкторы и деструкторы производных классов
- •2.3. Виртуальные функции
- •2.4. Абстрактные классы
- •2.5. Включение объектов
- •2.6. Включение и наследование
- •2.7. Множественное наследование
- •2.8. Локальные и вложенные классы
- •2.9. Пример программы для Microsoft Visual Studio
- •2.10. Упражнения
- •3. Перегрузка операций
- •3.1. Перегрузка унарных операций
- •3.2. Перегрузка бинарных операций
- •3.4. Перегрузка операции вызова функции
- •3.5. Перегрузка операции присваивания
- •3.6. Перегрузка операции new
- •3.7. Перегрузка операции delete.
- •3.8. Основные правила перегрузки операций
- •3.9. Примеры программ
- •4. Шаблоны функций и классов
- •4.1. Шаблоны функций
- •4.2. Шаблоны классов
- •4.3. Компонентные функции параметризованных классов
- •4.4. Примеры программ.
- •5. Обработка исключительных ситуаций
- •5.2. Получение дополнительной информации об исключении
- •5.3. Определение типа исключения
- •5.4. Иерархия исключений
- •5.5. Спецификация функций, обрабатывающих исключения
- •6. Потоковые классы
- •6.1. Библиотека потоковых классов
- •6.3. Стандартные потоки ввода-вывода
- •6.4. Форматирование
- •6.5. Манипуляторы
- •6.6. Ввод-вывод объектов пользовательских классов
- •6.7. Определение пользовательских манипуляторов
- •6.8. Пользовательские манипуляторы с параметрами
- •6.9. Использование макросов для создания манипуляторов
- •6.10. Состояние потока
- •6.11. Неформатированный ввод-вывод
- •6.12. Файловый ввод-вывод
- •7.1. Пространство имен
- •7.2. Динамическая идентификация типов
- •7.3. Безопасное приведение типа
- •8. Стандартная библиотека шаблонов
- •8.1. Введение в stl
- •Парадигму обобщенного программирования можно сформулировать следующим образом:
- •8.2. Итераторы
- •8.3. Классы-контейнеры
- •Iterator find(const key_type&k)
- •Iterator lower_bound(const key_type&k)
- •Iterator upper_bound(const key_type&k)
- •8.4. Контейнер vector
- •8.5. Многомерные массивы
- •8.6. Ассоциативные контейнеры
- •8.7. Объекты-функции
- •8.8. Алгоритмы
- •I. Немодифицирующие операции
- •II. Модифицирующие операции
- •III. Сортировка
- •IV. Работа с множествами
- •V. Минимумы и максимумы
- •VI. Перестановки
7.2. Динамическая идентификация типов
Динамическая идентификация типа (RTTI–Run-Time Type Identification) характерна для языков, в которых поддерживается полиморфизм. В этих языках возможны ситуации, в которых тип объекта на этапе компиляции неизвестен.
В С++ полиморфизм поддерживается через иерархии классов, виртуальные функции и указатели базовых классов. При этом указатель базового класса может использоваться либо для указания на объект базового класса, либо для указания на объект любого класса, производного от этого базового.
Информацию о типе объекта получают с помощью оператора typeid, при использовании которого следует подключить заголовочный файл <typeinfo.h>
Оператор typeid имеет две формы:
typeid (объект)
typeid (имя_типа)
Оператор typeid возвращает ссылку на объект типа type_info.
В классе type_info определены следующие открытые члены:
bool operator==(const type_info&ob)const;
bool operator!=(const type_info&ob)const;
const char* name()const;
Перегруженные операции == и != обеспечивают сравнение типов.
Функция name() возвращает указатель на имя типа.
Оператор typeid имеет одно ограничение. Он работает корректно только с объектами, у которых определены виртуальные функции. Когда оператор typeid применяют к неполиморфному классу (в классе нет виртуальной функции), получают указатель или ссылку базового типа.
Пример 7.2.1.
#include<iostream.h>
#include<typeinfo.h>
class Base{
virtual void f(){};
//…
};
class Derived: public Base{
//…
};
void main()
{int i;
Base ob,*p;
Derived ob1;
cout<<typeid(i).name(); //Выводится int
p=&ob1;
cout<<typeid(*p).name(); //Выводится Derived
}
Пример 7.2.2.
#include<iostream.h>
#include<typeinfo.h>
class Base{
virtual void f(){};
//…
};
class Derived: public Base{
//…
};
void WhatType(Base& ob)
{cout<< typeid(ob).name()<<endl;
}
void main()
{
Base ob;
Derived ob1;
WhatType(ob); // Выводится Base
WhatType(ob1); // Выводится Derived
}
Пример 7.2.3.
class X{
virtual void f(){};
};
class Y{
virtual void f(){};
};
void main()
{X x1,x2;
Y y1;
if(typeid(x1)==typeid(x2))cout<<“тип одинаков”;
else cout<<“тип не одинаков”;
if(typeid(x1)!=typeid(y1)) cout<<“тип неодинаков”;
…
if(typeid(x1)==typeid(X)) cout<<“x1 имеет тип X”;
…
}
При использовании указателей на объект, например typeid(*p), если p==NULL, то возникает исключительная ситуация bad_typeid.
7.3. Безопасное приведение типа
Приведение типа вида (имя_типа) выражение, определенное в стандарте языка С, может привести к ошибкам. Ошибка может произойти, когда вы попытаетесь привести один объект к другому при их несовместимости.
Пример 7.3.1.
struct A
{int i};
struct B
{char* s;}
void f(A* x)
{B* p=(B*)x;
cout<<p–>s;}
void main()
{B b;
A a,*r;
b.s=new char[5];
b.s=“abcd”;
a.i=15;
r=&a;
f(r);//Здесь ошибка
r=(A*)(&b);
f(r);//А так ошибки нет
}
Для решения проблемы безопасного приведения типов в С++ введены операторы:
dynamic_cast, static_cast, const_cast, reinterpret_cast.
Оператор dynamic_cast
Оператор dynamic_cast предназначен для безопасного приведения типа одного указателя или ссылки в другой. Основное назначение оператора dynamic_cast заключается в реализации приведения полиморфных типов.
Синтаксис оператора dynamic_cast:
dynamic_cast<целевой_тип>(выражение)
Здесь ‘целевой_тип’ – это тип, которым должен стать тип параметра ‘выражение’ после выполнения приведения типа. Оператор выполняется успешно, когда указатель (или ссылка) после приведения типа становится указателем на объект целевого типа либо на объект производного от целевого типа. При невозможности приведения результатом является либо NULL, если приводятся указатели, либо возбуждается исключительная ситуация bad_cast, если приводятся ссылки.
Пример 7.3.2.
Base* bp,b;
Derived* dp,d;
bp=&d;
dp= dynamic_cast<Derived*>(bp);
if(dp)cout<<“приведение прошло успешно”;
bp=&b;
dp=dynamic_cast<Derived*>(bp);
if(!dp)cout<<“приведение невозможно”;
Оператор dynamic_cast в некоторых случаях можно использовать вместо typeid.
Base* bp;
Derived* dp;
Приведение типов можно выполнить так
if (typeid(*bp)==typeid(Derived))dp=(Derived*)bp;
А можно и короче
dp=dynamic_cast<Derived*>(bp);
Оператор const_cast
const_cast<целевой_тип>(выражение)
Используется в том случае, когда вам необходимо удалить атрибут const из объекта.
void f(const int* p)
{int* v;
v=const_cast<int*>(p);
*v=2*(*v);
}
Оператор static_cast
static_cast<целевой_тип> (выражение)
Предназначен для выполнения операции приведения типов над объектами неполиморфных классов. Например, его можно использовать для приведения типа указателя базового класса к типу указателя производного класса. Кроме этого, он подойдет и для выполнения любой стандартной операции преобразования, но только не в динамическом режиме.
int i;
float f;
f=199.22;
i=static_cast<int>(f);
Оператор reinterpret_cast
reinterpret_cast<целевой_тип>(выражение)
Дает возможность преобразовать указатель одного типа в указатель совершенно другого типа. Он также позволяет приводить указатель к типу целого или целое к типу указателя. Это преобразование также опасно, как и старый С-стиль преобразования.
int i;
char* p=“Это строка”;
i=reinterpret_cast<int>(p);
cout<<i;
