Студопедия
Случайная страница | ТОМ-1 | ТОМ-2 | ТОМ-3
АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатика
ИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханика
ОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторика
СоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансы
ХимияЧерчениеЭкологияЭкономикаЭлектроника

ООП (Объектно-ориентированное программирование)

Основные понятия

ООП – методология программирования, которая основана на представлении программы в виде совокупности объектов, каждый из которых является реализацией определённого класса (типа), а кдассы (типы) образуют иерархию на принципах наследуемости.

Преимущества использования ООП:

1. уменьшение сложности ПО

2. повышение надёжности ПО

3. обеспечение возможности модификации отдельных компонентво ПО без изменения остальных его компонентов

4. обеспечение возможности повторного использования отдельных компонентов ПО

Инкапсуляция, наследование, полиморфизм.

Инкапсуляция

Инкапсуляция – механизм, котрый объединяет в одном классе данные, методы и свойства. Она позволяет изолировать класс из остальных частей программы и от неправильного использования. Этот механизм и позволяет создавать объекты. Принципы инкапсуляции преполагают также отказ от непосредственного обращения к пролям данных класса, хотя это в ряде случаев и возможно.

Наследование

Наследование – отношение между объектами, когда один объект повторяет структуру и поведение другого. (по Гради Бучу)

Наследование – механизм, посредством которого один объект может приобретать основные характеристики других объектов идобавлять к ним свои, характерные только для него.

Наследование позволяет создавать иерархию (сложную древовидную структуру) объектов, начиная с некоторого первоначального, и кончая преемственными классами, имеющие часть признаков от предыдущего. Все классы порождены от единственного родителя класса Object.

Полиморфизм

Полиморфизм - свойство различных объектов выполнять одно и то же действие по своему.

Полиморфизм - свойство, которе позволяет одно и то же имя использовать для решения нескольких схожих, но технически разных задач.

Полиморфизм – свойство классов решить схожие по смыслу проблемы разными способами.

Структура класса. Данные класса

Класс – определённый пользователем проблемно-ориентированный тип данных, описывающий внутреннюю структуру объектов, котрые являются его экземлярами. Объект (экземпляр класса) находится в таком же отношении к своему классу, в каком переменная находится по отношению к своему типу.

В состав класса входят данные и функции. В совокупности они называются членами класса. Данные, входящие в класс, называются данными-членами, или полями. Функции, принадлежащие классу, называют функциями-членами или методами. Поля + методы в одной конструкции -инкапсуляция.

Ключ класса: имя <базовый список> {список членов};

Ключ класса: class, struct или union. Имя класса определяет уникальный идентификатор внутри данного блока. Базовый список определяет классы, из которых данный класс наследует объекты и методы. В базовом списке также могут определяться спецификаторы доступа к наследуемым элементам. Список членов класса определяет собственные элементы класса. При описании членов классов возможно указан ие атрибутов управления доступом (спецификаторов доступа) элементам классов.

Такие атрибуты: public – члены класса могут использоваться любой функцией; private: соответствующие элементы могут использоваться только функциями-членами класса или дружественными функциями; protected: то же, что в private, но дополнительно элементы могут использоваться в порождённых классах и только если эти элеметы наследуются. По умолчанию элементы класса, объявленные с ключом class, имеют тип private. По умолчанию с ключом struct и union все члены класса имеют тип public.

Области доступности элементов класса

Данные-члены (поля) – набор взаимосвязанной информации, возможно различных типов, объединённых в один объект.

Данные-члены могут:

1) находиться в public, private, protected частях класса.

2) могут иметь статический класс памяти (static). Такие поля определяются в единственном экземпляре для всего класса, а не для каждого его объекта.

3) могут быть объявлены как const. Константные данные должны быть инициализированы в каждом определении конструктора. Имена полей, и начальные значения заключаются в скобки, отделяются от списка аргументов конструктора двоеточием.

4) могут быть переменными другого класса.

Инициализация полей при описании не допускается.

Классы

Могут быть глобальными (объявленными вне любого блока) и локальными (объявленными внутри блока – функции или другого класса)

Локальные классы

1) не могут иметь статических элементов;

2) методы этих классов могут быть описаны только внутри класса;

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

24.5. Функции-члены (методы класса) – функции, которые манипулируют данными-членами класса.

Функции-члены:

1)Имеют доступ ко всем полям своего класса.

2)могут быть в public, protected, private части класса.

3)могут быть определены внутри или вне объявления класса.

4)могут обращаться к полям или методам, объявленным после них.

5)имеют неявно объявленную переменную (указатель) this (он неявно используется внутри метода для ссылок на элементы объекта. Его явное указание целесообразно в случае, когда метод возвращает указатель или ссылку на вызвавший его объект, или имеется конфликт имен (когда имя поля класса совпадает с именем формального параметра метода)).

6)могут быть static.

7)могут быть объявлены как const (но не могут быть статическими)

8)могут быть виртуальными (но не могут быть статическими)

Статические и нестатические члены класса.

Статические элементы класса

С помощью модификатора static можно описать статические поля и методы класса. Их можно рассматривать как глобальные переменные или функции, доступные только в пределах области класса.

Нестатические же члены класса объявляются лишь как указатели или ссылки на объект своего класса.

Статические методы

Статические методы предназначены для обращения к статическим полям класса. Могут обращаться непосредственно только к статическим полям и вызывать только другие статические методы класса, потому что им не передается скрытый указатель this.

Эти методы не могут быть константными и виртуальными.

Использование статического метода

class A {

private:

Static int count;

Public

static void inccount(){count++};

};

A::int count;

void f(){

A a;

//a.count++;//так нельзя!!!!

a.inccount; //так можно

Константные и статические поля.

Статические поля

Поля класса могут быть объявлены как статические. Объявление поля с атрибутом static не является его определением – это поле (с помощью оператора расширения области видимости) должно быть определено дополнительно, и при этом можно задать начальное значение этого поля. Статическое поле может быть изменено либо с помощью статических методов, либо с помощью механизма инициализаторов (по крайней мере в системе программирования Microsoft Visual Studio).

Константные поля

Поля класса могут быть описаны как константные. Значение константного поля для вновь создаваемого объекта может быть задано в момент работы конструктора и в дальнейшем не может быть изменено. Занесение значений константных полей выполняется через механизм инициализаторов, например:

class MyClass {

Const int ID;

public:

MyClass(aID): ID(aID) { … }

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

MyClass(aID) {ID = aID; … }

Константные методы.

Константный объект

Значения полей такого объекта изменять запрещается. К нему применяются только константные методы.

Константный метод

1)объявляется с ключевым словом const после списка параметров;

2)не может изменять значения полей класса;

3)может вызывать только константные методы;

4)может вызываться для любых (не только константных методов).

Преимущество константного метода: дополнительный контроль компилятора в функциях, в которых объект передаётся по константной ссылке.

Дружественные функции и классы

Это функции, не являющиеся членами класса, но которым разрешен доступ ко всем членам класса (элитная группа с привилегиями).

Эти функции не являются членами класса, поэтому атрибуты доступа к ним не применимы.

class complex {…

friend my_func(complex&);

friend another_class::method(complex&, int);

Если все методы какого-то класса должны иметь доступ к скрытым полям другого, то весь такой класс можно объявить дружественным:

Friend class another_class;

Дружественная функция опредеяется вне класса.

Использование стража включения

Для предотвращения ошибок, связанных с повторным описанием класса, логично обрамлять описанеи класса стражем включения:

#ifndef__Point2D_defined__

#define__Point2D_defined__class Point 2D{…};

#endif

25. КОНСТРУКТОРЫ И ДЕСТРУКТОРЫ КЛАССОВ

Конструкторы. Назначение конструкторов.

Конструктор класса – функция-член этого класса, которая всегда вызывается компилятором после создания объекта класса. В конструкторе можно использовать данные-члены и функции-члены класса, а также указатель this.

Конструкторы должны быть:

1)совпадающими по имени с именем класса

2)без возвращаемого значения (не установлен тип возвращаемого значения)

3)без наследования

4)без модификаторов доступа const, volatite, virtual, static

5)может быть как внутренним, так и внешним методом

6)за счёт механизма перегрузки может быть создано несколько конструкторов, различающихся набором параметров

7)если ни одного конструктора не написано, реализуется так называемый конструктор по умолчанию без параметров. Этот конструктор не выполняет никаких дополнительный действий.

Class Point{

private: double x,y;

public:

Point(); //конструктор без параметров, который сбросит точку в начале подпрограммы (???)

Point(double,double); //конструктор, который в качетстве параметров передаёт координаты точек.

Point::Point() {x=0;y=0;}

Point::Point(double ax, double ay) {x=ax; y=ay;}

За счёт использования параметров по умолчанию можно обойтись одним конструктором:

class Point{

Point (double=0,double=0);

};

Конструктор по умолчанию

Конструктором по умолчанию называется конструктор, который можно определить 2 способами:

1)без параметров

2)со списком параметров, где все параметры имеют значения по умолчанию.

Класс может иметь только один конструктор по умолчанию. Такой конструктор только создаёт объект и вызывает конструкторы по умолчанию для вложения обеъктов и конструктор по умолчанию для базового класса (если такой существует)

Если класс имеет хотя бы один конструктор, то конструктор по умолчанию ……..

В разделе private можно помещать функции. Если в раздел private поместить конструктор, то нельзя будет объявить ни одного объекта этого класса.

Конструктор копирования

Конструктор копирования позволяет создать новый объект на основании уже существующего, например, при выполнении оператора

complex c4=c2;

Такой конструктор получает в качестве параметра ссылку, обычно константную, на объект того же класса:

complex::complex(const complex& C)

{

Re = C.Re;

Im = C.Im;

}

Если программист не указал конструктора копирования, автоматически созданный конструктор выполняет поэлементное копирование полей. Если же класс содержит указатели или ссылки, то это может послужить источником трудноуловимых ошибок.

Конструктор копирования вызывается:

1)при создании нового объекта с инициализацией существующего объекта.

2)при передаче в функцию объекта по значению

3)при выходе из функции, возвращающей объект

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

Список инициализаторов в конструкторе.

В реализации конструктора может быть задан список инициализации, который записывается после заголовка конструктора через знак:

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

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

Список инициализации обязателен:

1)если в качестве поля задаётся объект другого класса, для которого должен быть запущен конструктор;

2)для вызова конструктора предка при использовании механизма наследования.

3)для определения констант

4)при использовании ссылки список инициализации должен содержать вызовы конструкторов. При инициализации могут инициализироваться другие данные-члены

 

Class Demo{

Const int a;

Int b,c;

public:

Demo():a(0),b(1){c=2;};

};

Деструктор

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

Если деструктор не написан, вызывается деструктор по умолчанию, котоый не выполняет никаких дополнительных действий.

Деструктор должен:

1)не иметь аргументов

2)не возвращать значения

3)не наследоваться

4)не объявляться как const, volatite, static

Деструктор всегда один, и его нельзя перегрузить (не имеет аргументов). Он служит для освобождения захваченных ресурсов, т.е. он не пишется, если ресурсы освобождать не нужно. В этом случае компилятор сгенерирует деструктор по умолчанию, который выполнит следующее:

1)вызывает деструктор данных-членов класса

2)вызывает деструкторы базовых членов класса (если они существуют)

Деструктор вызвается неявно в случаях:

1)для static объектов при завершении программы

2)для локальных объектов при выходе из блока

3)при применении оператора delete к указателю на объект.

4)при раскрутке стека для обработки искючений.

void my_func(){

Point B1; Point* t1=new Point(10,-3);

Point * t2=new Point(0,16);

delete t1; //деструктор для объекта, адрес которого в t1;

return; //вызывает деструктор для локального объекта B1, время которого истекает

}

Использовние указателей при работе с классами

Указатель this. Указатели на члены класса.

Указатели на нестатические члены класса

При определении указателей на нестатические члены класса следует учитывать, как тип члена класса, так и тип самого класса. При определении таких указателей тип класса с оператором разрешения области видимости ставится перед именем указателя.

Для данных: тип_члена_класса тип_класса::*имя_указателя(список_параметров)

Для функций: возвращаемый_тип (тип_класса::*имя_указателя)(список_параметров)

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

Для доступа к нестатическим членам класса через указатель используются операторы (.*) для объектов и для ссылок на объекты класса и (->*) для указателя на объект.

Struct Demo

{ int count;

int Count() {return count;};

};

Int main()

{

Demo d;

Demo* dp=&d;

int Demo::*ip; //указатель на член-данные

int Demo::*fp(c) //указатель на член-функцию

ip=&Demo::Count; // инициализация

fp=Demo::Count; // -//-

Указатель this.

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

Const Point

Point::greater(const Point&c)const

{if (module()->c.module())

return *this;

return c;}

Указатели на статические члены класса

Между указателями на статические и нестатические члены класса есть разница. Синтаксис указателя на член класса не используется для обращения к статичяескому члену. Статические члены – это глобальные объекты и функции, принадлежащие классу. Указатели на них - обычные указатели. Объявление указателя на статический член класса выглядит так же, как и для указателя на объект, не являющийся членом класса. Для разыменования указателя никакой объект не требуется.

ПЕРЕГРУЗКА ОПЕРАЦИЙ

Реализация операции как функции. Перегрузка операций.

В языке С++ операторы рассматриваются как функции, имеющие следующий синтаксис:

Возвращаемый_тип operator имя_оператора (список_параметров)

Поэтому операторы, как и функции, можно перегружать.

Перегруженным операторам разрешается определять как члены, так и не члены классов. Один оператор может быть перегружен несколько раз.

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

Операторы могут вызываться 2 способами: явно, как функции, и неявно, используя операторную запись.

Ограничения на перегрузку операций.

Перегрузка операторов должна удовлетворять условиям:

1) нельзя вводить новые операции

2) не позволяет изменять приоритеты операций, так что перегруженная версия операции будет иметь тот же приоритет в последовательности вычисления выражений, что и оригинальная базовая операция.

3)не допускается перегрузка операторов со встроенными типами данных в качестве аргументов, по крайней мере, 1 аргумент перегруженного оператора должен иметь тип данных пользователя.

4)нельзя изменять число аргументов

5)не может быть аргументов по умолчанию, за исключением () при вызове функции

Операции, которые не могут быть перегружены:

1)Операция разрешения контекста ::

2)Условная операция ?:

3)Операция прямого выбора члена.

4)Операция размера объекта sizeof

5)Разыменующий указатель на член класса. *

6)Препроцессорные символы #,##

7)Операторы преобразования типов данных static_cast, dynamic_cast

Перегрузка унарных операций.

Предложенные унарные операторы могут быть перегружены как нестатические члены класса без параметров.

Возвращаемый_тип operator @(также можно перегрузить тут вместо @ ~! - + * & ++ -- и т.д.) (параметр)

1. Point & operator – (){

x=-x; y=-y;

return *this; }

2. Point operator – (const Point&e){

return Point(-e.x,-e.y);}

Перегрузка бинарных операторов

Бинарные операторы могут быть перегружены как нестатические члены класса с одним параметром.

Point & Point:: operator + (Point &p)

{x=this->x+p.x;

y=this->y+p.y;

return *this; }

Или как операторы – не члены класса

возвращаемый_тип operator – (также, помимо - можно перегрузить + * / % == <= >=!= && || << >>)(параметр)

При перегрузке бинарных операторов следует учитывать симметричность их параметров. Если аргументы бинарного оператора являются объектами одного класса, то лучше перезагрузить как friend, оператор с 2 параметрами.

friend point operator + (point &, point &);

point operator + (point & p1, point & p2)

{point p;

p.x=p1.x+p2.x;

p.y=p1.y+p2.y;

return p;}

Метод сложения с константой слева (аналогично справа)

friend point operator + (int n, point &p2);

point operator + (int n, point &p2)

{point p;

p.x=n+p2.x;

p.y=n+p2.y;

return p;}

Перегрузка оператора присваивания

имя_класса operator = (const имя_класса &);

Ссылка при возврате позволяет использовать последовательное присваивание. Если оператор присваивания не опреден в классе, то компилятор генерирует оператор присваивания по умолчанию, который выполняет почленное присваивание членов-данных класса.

point & operator = (const point &e)

{x=e.x;

y=e.y;

return*this;}

Если при присваивании объектов класса дополнительно к почленному присваиванию членов-данных требуется выполнить ещё какие-либо действия, то оператор присваивания нужно обязательно перезагрузить.

Замечания:

1)При реализации присваивания должна проверяться возможность присваиваняи объекта самому себе.

2)Если оператор присваивания и конструктор копирования содержат много общего кода, то пишут отдельную закрытую функцию, реализуюшую этот код.

point & point:: operator = (const point & p)

{ if(this==&p)

return *this;

x=p.x;

y=p.y;

return *this;}

Перегрузка операторов += -= *= /= %=

point & point:: operator += (const point & p)

{x=this->x+p.x;

y=this->y+p.y;

return this;}

Перегрузка оператора индексирования

Оператор индексирвоания определяется для контейнерного класса, из которого извлекаются отдельные элементы. Оператор индексирования может появляться как слева, так и справа от оператора присваивания

int & operator [](const int i)

{return p[i];}

Перегрузка оператора вызова функции

Перегруженный оператор вызова функции () вызывается путём применения к объекту класса, в котором он определён. Возможно любое количество элементов. Допускаются аргументы по умолчанию. Т.к. в этом случае объект может быть использован как функция, то он называется function object. Класс, в котором определён хотя бы один оператор вызова функции, называется функциональным классом. Функциональный класс может не иметь других полей и методов. Функциональные классы используются в алгоритмах из библиотеки STL.

Class Increment

{ public: int operator () (int & n) {return ++n;} };

Int main()

{ Increment inc;

int n=1;

Inc();

return 1; }

Перегрузка операторов приведения типа.

Имеет прототип operator тип()

Тип – задаёт тип данных, к которому приводится объект. Этот тип может быть как стандартным, так и пользовательским. Нет возвращаемого значения.

Point:: operator double()

{ return Module(); }

Point p(10,5);

cout<<operator*(3,p); //можно так

cout<<3*(double)p; //и можно так

Перегрузка операторов ввода-вывода

Перегруженные операторы >> << должны определяться как friend операторы класса.

friend istream & operator >> (istream &, имя класса &);

friend ostream & operator << (ostream &, имя класса &);

Это позволяет избежать путаницы с общепринятым написанием этих операторов.

27.10 Перегрузка операторов ++ --

Чтобы отличить префиксные от соттвествующих постфиксных, в объявлении последних вводят дополнительный параметр типа int

point& point::operator ++ (int k) //постфикс

{ point p(0,0); //конструктор; можно было было сделать по умолчанию point p;

p.x.=x++;

p.y=y++;

return p;}

point&point::operator ++()

{ ++x;

++y;

return *this:}

ОПЕРАТОРЫ ПРИВЕДЕНИЯ ТИПОВ.

const_cast – Операция служит для удаления модификатора const. Как правило, она используется

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

Не имеющего модификатора const.

const_cast <тип> (выражение)

Обозначеyyый тип должен быть таким же, как и тип выражения, за исключением

модификатора const. Обычно это указатель. Операция формирует результат указанного

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

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

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

Операция const_cast используется в том случае, когда программист уверен, что в теле функции значение, на которое ссылается указатель, не изменяется. Естественно, если есть возможность добавить к описанию формального параметра модификатор const, это предпочтительнее использования преобразования типа при

вызове функции.

 

dynamic_cast – применяется дял преобразования указателей родственных классов иерархии, в основном указателя базового класса в указатель на производный класс, при этом во время выполнения программы производится проверка допустимости преобразования.

dynamic_cast <тип*> (выражение)

Выражение должно быть указателем или ссылкой на класс, тип — базовым или производным

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

в противном случае для указателя результат равен нyлю, а для ссылки порождается

исключение bad_cast. Если заданный тип и тип выражения не относятся к одной иерархии, преобразование не допускается. Преобразование из базового класса в производный называют понижающим (downcast), так как графически в иерархии наследования принято изображать производные классы ниже базовых. Приведение из производного класса в базовый называют повышающим (upcast), а приведение между производными классами одного базового или, наоборот, между базовыми классами одного производного — перекрестным (crosscast).

 

Операция reinterpret_cast применяется для преобразования не связанных между собой типов, например, указателей в целые или наоборот, а также указателей типа void* в конкретный тип. При этом внутреннее представление данных остается неизменным, а изменяется только точка зрения компилятора на данные.

reinterpret_cast <тип> (выражение)

Результат операции имеет указанный тип, который может быть ссылкой, указателем, целым или вещественным типом. Различие между static_cast и reinterpret_cast позволяет компилятору производить минимальную проверку при использовании static_cast, а программисту —

обозначать опасные преобразования с помощью reinterpret_cast. Результат преобразования остается на совести программиста.

 

Операция static_cast используется для преобразования типа на этапе компиляции

между:

• целыми типами;

• целыми и вещественными типами;

• целыми и перечисляемыми типами;

• указателями и ссылками на объекты одной иерархии, при условии, что оно однозначно и не связано с понижающим преобразованием виртуального базового класса.

 

static_cast <тип> (выражение)

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

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

В общем случае использование для преобразования указателей родственных

классов иерархии предпочтительнее использовать операцию dynamic_cast. В этом случае если преобразование возможно на этапе компиляции, генерируется тот же код, что и для static_cast. Кроме того, dynamic_cast допускает перекрестное преобразование, нисходящее приведение виртуального базового класса и производит

проверку допустимости приведения во время выполнения.

 

\

 

ТИПЫ ПРЕОБРАЗОВАНИЙ

Понижающее преобразование

Чаще всего операция dynamic_cast применяется при понижающем преобразовании

— когда компилятор не имеет возможности проверить правильность приведения.

Производные классы могут содержать функции, которых нет в базовых классах. Для их вызова через указатель базового класса нужно иметь уверенность, что этот указатель в действительности ссылается на объект производного класса. Такая проверка производится в момент выполнения приведения типа с использованием RTTI (run-time type information) — «информации о типе во время выполнения программы». Для того чтобы проверка допустимости могла быть выполнена, аргумент операции dynamic_cast должен быть полиморфного типа, то есть иметь хотя бы один виртуальный метод. Для полиморфного объекта реализация операции dynamic_cast весьма эффективна, поскольку ссылка на информацию о типе объекта заносится в таблицу виртуальных

методов, и доступ к ней осуществляется легко.

С точки зрения логики требование, чтобы объект был полиморфным, также оправдано:

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

класс В и производный от него класс С, в котором определена функция f2. Для того, чтобы вызывать ее из функции demo только в случае, когда последней передается указатель на объект производного класса, используется операция

dynamic_cast с проверкой результата преобразования:

#include <iostream.h>

#include <typeinfo.h>

class B{

public: virtual void fl () { };};

class C: public B{

public: void f2(){cout «"f2":};};

void demo(6* p){

C* с = dynamic_cast<C*>(p);

if (c) c->f2();

else cout «"Передан не класс С";}

int main(){

В* b = new В;

demo(b); // Выдается сообщение "Передан не класс С"

С* с - new С;

demo(c); // Выдается сообщение "f2" (правильно)

return 0;}

При использовании в этом примере вместо dynamic_cast приведения типов в стиле

С, например:

С* с = (С*) р;

проконтролировать допустимость операции невозможно, PI если указатель р на самом деле не ссылается на объект класса С, это приведет к ошибке. Другим недостатком приведения в стиле С является невозможность преобразования

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

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

базового класса:

#include <iostream.h>

#include <typeinfo.h>

class A{

public: virtual ~A(){};}

c]ass B: public virtual A{};

class C: public virtual A{};

class D: public B. public C{};

void demo(A *a){

D* d = dynamic_cast<D*>(a);

i f (d) {... }

/ / A

/ / / \

/ / В С

/ / \ /

/ / D

int main(){D *d = new D; demo(d);return 0;}

Повыщающее преобразование

Выполнение с помощью операции dynamic_cast повышающего преобразования

равносильно простому присваиванию:

class В{ / *... * / };

class С: public В{ /*... */ };

С* с = new С;

В* b = dynamic_cast<B*>(c); // Эквивалентно В* b = с;

29.3. Перекрёстное преобразование

Операция dynamic_cast позволяет выполнять безопасное преобразование типа между производными классами одного базового класса, например:

#include <iostream.h>

#include <typeinfo.h>

class B{

public: virtual void fl () { };

};

class C: public B{

public: void f2(){... };

};

class D: public B{... };

void demo(D* p){

C* с = dynamic_cast<C*>(p);

if(c)c->f2();

else cout «" не выполнено ";

}

int main(){

B* b = new C: demo((D*)b);

return 0:

}

Классы С и D являются производными от класса В. Функции demo передается указатель на класс D, являющийся на самом деле указателем на «братский» для него класс С, поэтому динамическое преобразование типа из D в С в функции demo завершается успешно.

При необходимости можно осуществить преобразование между базовыми классами

одного производного класса, например:

#include <iostream.h>

finclude <typeinfo.h>

class B{

public: virtual void fl () {... }; / / В С

}: // \ /

class C{ // D

public: virtual void f2(){... };

};

class D: public B, public C{};

void demo(B* b){

С* с = dynamic_cast<C*>(b);

if(c)c->f2();

}

int main(){

D* d = new D; demo(d);

Return 0;

}

Класс D является потомком В и С, поэтому содержит методы обоих классов. Если в функцию demo передается на самом деле указатель не на В, а на D, его можно преобразовать к его второму базовому классу С.

 

ВНИМАНИЕ!!! Также смотреть static_cast!


Дата добавления: 2015-10-29; просмотров: 121 | Нарушение авторских прав


Читайте в этой же книге: H. Числа Армстронга | J. Числа Смита | Добавление элементa в середину. |
<== предыдущая страница | следующая страница ==>
Задача о рюкзаке| Рынок. Класификация рынка.

mybiblioteka.su - 2015-2024 год. (0.112 сек.)