Наследование и виртуальные функции 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Наследование и виртуальные функции



Практикум на ЭВМ

Программирование на С++

Часть 3

 

 

Уфа 2008


 

Составитель:

Рыков В.И. Практикум на ЭВМ. Программирование на С++.. Часть 2. /Издание Башкирского ун-та. - Уфа 2008. - №№ c.

 

Работа посвящена методологии программирования на языке С++.

Содержит сведения технологии объектного программирования. Содержит тексты задач и, в необходимых случаях, указания по технологии их решения. Методика программирования и кодирования программ для каждого типа задач изложена в виде законченных примеров.

Является методическим обеспечением лабораторных занятий по дисциплине «Практикум на ЭВМ».

.

 

2008

Предисловие.. 5

1  Наследование и виртуальные функции.. 5

1.1 Цель. 5

1.2 Основное содержание работы. 5

1.3 Краткие теоретические сведения. 5

1.4 Порядок выполнения работы. 11

1.5 Методические указания. 11

1.6 Содержание отчета. 12

1.7 Варианты заданий. 12

2  Иерархия объектов и группа. итераторы... 13

2.1 Цель. 13

2.2 Основные теоретические сведения. 13

2.3 Порядок выполнения работы. 17

2.4 Методические указания. 17

2.5 Содержание отчета. 19

2.6 Варианты задания. 19

3  Перегрузка операций.. 21

3.1 Цель. 21

3.2 Основное содержание работы. 21

3.3 Краткие теоретические сведения. 21

3.4 Порядок выполнения работы. 24

3.5 Методические указания. 24

3.6 Содержание отчета. 25

3.7 Варианты заданий. 26

4  Шаблоны функций и классов.. 31

4.1 Цель. 31

4.2 Основное содержание работы. 31

4.3 Краткие теоретические сведения. 31

4.4 Методические указания. 33

4.5 Содержание отчета. 34

4.6 Варианты заданий. 35

5  Потоковые классы... 38

5.1 Цель. 38

5.2 Основное содержание работы. 38

5.3 Основные теоретические сведения. 38

5.4 Порядок выполнения работы. 46

5.5 Методические указания. 47

5.6 Содержание отчета. 48

6  Стандартная библиотека шаблонов.. 48

6.1 Цель. 48

6.2 Основное содержание работы. 48

6.3 Основные теоретические сведения. 49

6.4 Порядок выполнения работы. 58

6.5 Методические указания. 59

6.6 Содержание отчета. 60

6.7 Варианты заданий. 60

7  Графика.. 61

7.1 Цель. 61

7.2 Основное содержание работы. 61

7.3 Краткие теоретические сведения. 61

7.4 Порядок выполнения работы. 69

7.5 Методические указания. 69

7.6 Содержание отчета. 70

7.7 Варианты заданий. 70

8  Шаблоны программирования.. 71

8.1 Цель. 71

8.2 Основное содержание работы. 71

8.3 Краткие теоретические сведения. 71

8.4 Порядок выполнения работы.. 73

8.5 Методические указания. 73

8.6 Содержание отчета.. 77

8.7 Варианты заданий.. 77

9  Исключительные ситуации.. 77

9.1 Цель. 77

9.2 Содержание работы. 77

9.3 Краткие теоретические сведения. 77

9.4 Методические указания. 18

9.5 Варианты заданий. 18

10 Классы и объекты в С ++. 19

10.1  Цель. 19

10.2  Основное содержание работы. 19

10.3  Краткие теоретические сведения. 19

10.4  Порядок выполнения работы. 23

10.5  Методические указания. 24

10.6  Содержание отчета. 26

10.7  Варианты заданий. 26

2. Список литературы... 28


Предисловие

Цель практикума - закрепить знания, полученные при изучении теоретической части курсов и получить практические навыки разработки объектно-ориентированных программ. Практикум охватывает все разделы объектно-ориентированного программирования на языке С++ и включает выполнение 10 лабораторных работ. Первые работы связаны с базовыми понятиями С++, такими как объекты и классы, наследование, полиморфизм и виртуальные функции, обработка событий. Следующие работы посвящены профессиональному программированию на С++ и охватывают разделы профессионального программирования, такие как перегрузка операций, шаблоны, потоковые классы, стандартная библиотека шаблонов и исключительные ситуации.

Последняя работа посвящена технологии моделирования конкретной предметной области средствами технологии объектов

Наследование и виртуальные функции

Цель.

Получить практические навыки формирования иерархии классов и использования статических компонентов класса.

1.2 Основное содержание работы.

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

Указатель this.

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

имя_класса *const this = адрес_объекта

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

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

Примером широко распространенного явного использования this являются операции со связанными списками.

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

Наследование - это механизм получения нового класса на основе уже существующего. Существующий класс может быть дополнен или изменен для создания нового класса.

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

Наследуемые компоненты не перемещаются в производный класс, а остаются в базовых классах.

В иерархии производный объект наследует разрешенные для наследования компоненты всех базовых объектов (public, protected).

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

private член класса может использоваться только функциями – членами данного класса и функциями – “друзьями” своего класса. В производном классе он недоступен.

protected – то же, что и private, но дополнительно член класса с данным атрибутом доступа может использоваться функциями-членами и функциями – “друзьями” классов, производных от данного.

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

Следует иметь в виду, что объявление friend не является атрибутом доступа и не наследуется.

Синтаксис определения производного класса:

class имя_класса: список_базовых_классов

{список_компонентов_класса};

В производном классе унаследованные компоненты получают статус доступа private, если новый класс определен с помощью ключевого слова class, и статус public, если с помощью struct.

Явно изменить умалчиваемый статус доступа при наследовании можно с помощью атрибутов доступа – private, protected и public, которые указываются непосредственно перед именами базовых классов.

 

Конструкторы и деструкторы производных классов.

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

Например.

class Basis

{

int a,b;

public:

Basis(int x,int y)

{

a=x;b=y;

}

};

class Inherit:public Basis

{

int sum;

public:

Inherit(int x,int y, int s):Basis(x,y){sum=s;}

};

Объекты класса конструируются снизу вверх: сначала базовый, потом компоненты-объекты (если они имеются), а потом сам производный класс. Таким образом, объект производного класса содержит в качестве подобъекта объект базового класса.

Уничтожаются объекты в обратном порядке: сначала производный, потом его компоненты-объекты, а потом базовый объект.

Таким образом, порядок уничтожения объекта противоположен по отношению к порядку его конструирования.

 

Виртуальные функции.

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

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

Пример.

class base

{

public:

virtual void print(){cout<<“\nbase”;}

};

 

class dir: public base

{

public:

void print(){cout<<“\ndir”;}

};

void main ()

{

base B,*bp = &B;

dir D,*dp = &D;

base *p = &D;

bp –>print(); // base

dp –>print(); // dir

p –>print(); // dir

}

 

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

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

Виртуальными могут быть только нестатические функции-члены.

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

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

 

Абстрактные классы.

Абстрактным называется класс, в котором есть хотя бы одна чистая (пустая) виртуальная функция.

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

virtual тип имя_функции (список_формальных_параметров) = 0;

Чистая виртуальная функция ничего не делает и недоступна для вызовов. Ее назначение – служить основой для подменяющих ее функций в производных классах. Абстрактный класс может использоваться только в качестве базового для производных классов.

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

Пример.

class Base

{

public:

Base();                 // конструктор по умолчанию

Base(const Base&); // конструктор копирования

virtual ~Base(); // виртуальный деструктор

virtual void Show()=0; // чистая виртуальная функция

// другие чистые виртуальные функции

protected: // защищенные члены класса

private:

// часто остается пустым, иначе будет мешать будущим разработкам

};

class Derived: virtual public Base

{

public:

Derived();                           // конструктор по умолчанию

Derived(const Derived&);  // конструктор копирования

Derived(параметры);         // конструктор с параметрами

virtual ~Derived();              // виртуальный деструктор

void Show();                       // переопределенная виртуальная функция

// другие переопределенные виртуальные функции

// другие перегруженные операции

protected:

// используется вместо private, если ожидается наследование

private:

// используется для деталей реализации

};

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

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

Одно из преимуществ абстрактного метода является чисто концептуальным: программист может мысленно наделить нужным действием абстракцию сколь угодно высокого уровня. Например, для геометрических фигур мы можем определить метод Draw, который их рисует: треугольник TTriangle, окружность TCircle, квадрат TSquare. Мы определим аналогичный метод и для абстрактного родительского класса TGraphObject. Однако такой метод не может выполнять полезную работу, поскольку в классе

TGraphObject просто нет достаточной информации для рисования чего бы то ни было. Тем не менее присутствие метода Draw позволяет связать функциональность (рисование) только один раз с классом TGraphObject, а не вводить три независимые концепции для подклассов TTriangle, TCircle, TSquare.

Имеется и вторая, более актуальная причина использования абстрактного метода. В объектно-ориентированных языках программирования со статическими типами данных, к которым относится и С++, программист может вызвать метод класса, только если компилятор может определить, что класс действительно имеет такой метод. Предположим, что программист хочет определить полиморфную переменную типа TGraphObject, которая будет в различные моменты времени содержать фигуры различного типа. Это допустимо для полиморфных объектов. Тем не менее компилятор разрешит использовать метод Draw для переменной, только если он сможет гарантировать, что в классе переменной имеется этот метод. Присоединение метода Draw к классу TGraphObject обеспечивает такую гарантию, даже если метод Draw для класса TGraphObject никогда не выполняется. Естественно, для того чтобы каждая фигура рисовалась по-своему, метод Draw должен быть виртуальным.

Порядок выполнения работы.

1. Определить иерархию классов (в соответствии с вариантом).

2. Определить в классе статическую компоненту - указатель на начало связанного списка объектов и статическую функцию для просмотра списка.

3. Реализовать классы.

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

5. Сделать соответствующие методы не виртуальными и посмотреть, что будет.

6. Реализовать вариант, когда объект добавляется в список при создании, т.е. в конструкторе (смотри пункт 6 следующего раздела).

Методические указания.

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

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

3. Компонентные данные класса специфицировать как protected.

4. Пример определения статических компонентов:

a. static person* begin; // указатель на начало списка

b. static void print(void); // просмотр списка

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

6. Для добавления объекта в список предусмотреть метод класса, т.е. объект сам добавляет себя в список. Например, a.Add() - объект a добавляет себя в список.

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

7. Список просматривать путем вызова виртуального метода Show каждого объекта.

8. Статический метод просмотра списка вызывать не через объект, а через класс.

9. Определение классов, их реализацию, демонстрационную программу поместить в отдельные файлы.

Содержание отчета.

1. Титульный лист: название дисциплины; номер и наименование работы; фамилия, имя, отчество студента; дата выполнения.

2. Постановка задачи. Следует дать конкретную постановку, т.е. указать, какие классы должны быть реализованы, какие должны быть в них конструкторы, компоненты-функции и т.д.

3. Иерархия классов в виде графа.

4.  Определение пользовательских классов с комментариями.

5..Реализация конструкторов с параметрами и деструктора.

6. Реализация методов для добавления объектов в список.

7. Реализация методов для просмотра списка.

8. Листинг демонстрационной программы.

9. Объяснение необходимости виртуальных функций. Следует показать, какие результаты будут в случае виртуальных и не виртуальных функций.

Варианты заданий.

Перечень классов:

1. студент, преподаватель, персона, завкафедрой;

2. служащий, персона, рабочий, инженер;

3. рабочий, кадры, инженер, администрация;

4. деталь, механизм, изделие, узел;

5. организация, страховая компания, судостроительная компания, завод;

6. журнал, книга, печатное издание, учебник;

7. тест, экзамен, выпускной экзамен, испытание;

8. место, область, город, мегаполис;

9. игрушка, продукт, товар, молочный продукт;

10. квитанция, накладная, документ, чек;

11. автомобиль, поезд, транспортное средство, экспресс;

12. двигатель, двигатель внутреннего сгорания, дизель, турбореактивный двигатель;

13. республика, монархия, королевство, государство;

14. млекопитающие, парнокопытные, птицы, животное;

15) корабль, пароход, парусник, корвет.

Цель.

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

Группа.

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

Примеры групп:

1. Окно в интерактивной программе, которое владеет такими элементами, как поля ввода и редактирования данных, кнопки, списки выбора, диалоговые окна и т.д.

2. Агрегат, состоящий из более мелких узлов.

3. Огород, состоящий из растений, системы полива и плана выращивания.

4. Некая организационная структура (например, ФАКУЛЬТЕТ, КАФЕДРА, СТУДЕНЧЕСКАЯ ГРУППА).

Мы отличаем “группу” от “контейнера”. Контейнер используется для хранения других данных. Примеры контейнеров: объекты контейнерных классов библиотеки STL в C++ (массивы, списки, очереди).

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

Группа дает второй вид иерархии (первый вид - иерархия классов, построенная на основе наследования) - иерархию объектов (иерархию типа целое/часть), построенную на основе агрегации.

Реализовать группу можно несколькими способами:

1. Класс “группа” содержит поля данных объектного типа. Таким образом, объект “группа” в качестве данных содержит либо непосредственно свои элементы, либо указатели на них

class TWindowDialog: public TGroup

  {

   protected: 

  TInputLine input1;

  TEdit edit1;

  TButton button1;

  /*другие члены класса*/

  };

Такой способ реализации группы используется в C++ Builder.

 

2. Группа содержит член-данное last типа TObject*, который указывает на начало связанного списка объектов, включенных в группу. В этом случае объекты должны иметь поле next типа TObject*, указывающее на следующий элемент в списке.

3. Создается связанный список структур типа TItem:

  struct TItem

  {TObject* item;

   TItem* next;};

Поле item указывает на объект, включенный в группу. Группа содержит поле last типа TItem *, которое указывает на началосвязанного списка структур типа TItem.   

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

Методы группы.

Имеется два метода, которые необходимы для функционированиягруппы:

1) void Insert(TObject* p);

Вставляет элемент в группу.

2) void Show();

Позволяет просмотреть группу.

Кроме этого группа может содержать следующие методы:

1) int Empty();

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

2) TObject* Delete(TObject* p);

Удаляет элемент из группы, но сохраняет его в памяти.

3) void DelDisp(TObject* p);

Удаляет элемент из группы и из памяти.

 

Иерархия объектов.

Иерархия классов есть иерархия по принципу наследования, т.е. типа “это есть разновидность того”. Например, “рабочий есть разновидность персоны”, “автомобиль” есть разновидность “транспортного средства”. В отличие от этого иерархия объектов - это иерархия по принципу вхождения, т.е. типа “это есть часть того”. Например, “установка - часть завода”, “двигатель” - часть “автомобиля”. Таким образом, объекты нижнего уровня иерархии включаются в объекты более высокого уровня, которые являются для них группой.

 

Итератор.

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

Fo r all элементов набора { действия}

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

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

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

Определим тип указателя на функцию следующим образом:

typedef void(*PF)(TObject*,< дополнительные параметры>);

Функция имеет обязательный параметр типа TObject или TObject *, через который ей передается объект, для которого необходимо выполнить определенные действия.

Метод-итератор объявляется следующим образом:

void TGroup:: ForEach(PF action,< дополнительные параметры >);

где

action - единственный обязательный параметр-указатель на функцию, которая вызывается для каждого элемента группы;

дополнительные параметры - передаваемые вызываемой функции параметры.

Затем определяется указатель на функцию и инициализируется передаваемой итератору функцией.

PF pf= myfunc;

 

Тогда итератор будет вызываться, например, для дополнительного параметра типа int, так:

gr. ForEach(pf,25);

Здесь gr - объект-группа.

Порядок выполнения работы.

1. Дополнить иерархию классов лабораторной работы № 1 классами “группа”.

Например, для предметной области ФАКУЛЬТЕТ можно предложить классы “ факультет ”, “ студенческая группа ”, “ кафедра ”. Рекомендуется создать абстрактный класс - “ подразделение ”, который будет предком всех групп и абстрактный класс TObject, находящийся во главе всей иерархии.

2. Написать для класса-группы метод-итератор.

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

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

Методические указания.

1. Класс-группа должен соответствовать иерархии классов лабораторной работы № 1, т.е. объекты этих классов должны входить в группу. Например, для варианта 1 может быть предложена следующая иерархия классов:

 

                                  Tobject (абстр. класс)

 


Персона (абстр. класс) Подразделение (абстрактная группа)

     
 

 


Студент Преподаватель Кафедра  Студенческая группа

 

                  Завкафедрой                   Факультет

 

 

При этом иерархия объектов будет иметь следующий вид:

 

 

Факультет

 


Студ_ группа_1 Студ_ группа_ N Кафедра_1     Кафедра_ N

             
 
     


Студенты        Студенты  Преподаватели Преподаватели

 

                                                          Завкафедрой  Завкафедрой

 

2. Для включения объектов в группу cледует использовать третий способ (через связанный список структур типа TItem).

3. Пример определения добавленных абстрактных классов:

class TObject

{

public:

virtual void Show()=0;

};

class TDepartment:public TObject // абстрактный класс -группа

{

protected:

char name[20]; // наименование

TPerson* head; // руководитель

TItem* last; // указатель на начало св. списка структур TItem

public:

TDepartment(char*,TPerson*);

TDepardment(TDepartment&);

~ TDepartment();                

char* GetName(); 

TPerson* GetHead();

void SetName(char* NAME);

void SetHead(TPerson* p);

void Insert(TObject* p);   

virtual void Show()=0;                      

};

4. Иерархия объектов создается следующим образом (на примере ФАКУЛЬТЕТА):

а) создается пустой ФАКУЛЬТЕТ,

б) создается пустая КАФЕДРА,

в) создаются ПРЕПОДАВАТЕЛИ и включаются в КАФЕДРУ,

г) КАФЕДРА включается в ФАКУЛЬТЕТ,

д) тоже повторяется для другой кафедры,

 

е) создается пустая СТУДЕНЧЕСКАЯ ГРУППА,

ж) создаются СТУДЕНТЫ и включаются в СТУДЕНЧЕСКУЮ ГРУППУ,

з) СТУДЕНЧЕСКАЯ ГРУППА включается в ФАКУЛЬТЕТ,

и) тоже повторяется для другой студенческой группы.

5. Удаляется ФАКУЛЬТЕТ (при вызове деструктора) в обратном порядке.

6. Метод-итератор определяется в неабстрактных классах-группах на основе выбранных запросов.

Например, для класса TStudentGroup может быть предложен итератор void TStudentGroup::ForEach(PF action, float parametr);

где action - указатель на функцию, которая должна быть выполнена для всех объектов, включенных в группу (в данном случае для всех СТУДЕНТОВ), parametr- передаваемая процедуре дополнительная информация.

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

void MyProc(TObject* p,float rate)

{

if (((TStudent*)p) ->GetGrade()>=rate) cout<<(((TStudent*)p) ->GetName());

}

7. Студент определяет передаваемую итератору функцию на основе запросов, которые должны быть выполнены вызовом итератора. Варианты запросов приведены в приложении.

Содержание отчета.

1. Титульный лист.

2. Постановка задачи.

3. Иерархия классов.

4. Иерархия объектов.

5. Определение классов (добавленных или измененных по сравнению с лабораторной работой № 1).

6. Реализация для одного не абстрактного класса-группы всех методов.

7. Реализация итератора.

8. Реализация передаваемой итератору функции.

9. Листинг демонстрационной программы.

 

2.6 Варианты задания.

Количество мужчин (женщин).

16. Количество студентов на указанном курсе.

Перегрузка операций

Цель.

Получить практические навыки работы в среде BC++5.02 и создания EasyWin-программы. Получить практические навыки создания абстрактных типов данных и перегрузки операций в языке С++.

3.2 Основное содержание работы.

Определить и реализовать класс - абстрактный тип данных. Определить и реализовать операции над данными этого класса. Написать и выполнить EasyWin-программу полного тестирования этого класса.

 

Абстрактный тип данных (АТД).

АТД - тип данных, определяемый только через операции, которые могут выполняться над соответствующими объектами безотносительно к способу представления этих объектов.

АТД включает в себя абстракцию как через параметризацию, так и через спецификацию. Абстракция через параметризацию может быть осуществлена так же, как и для процедур (функций); использованием параметров там, где это имеет смысл. Абстракция через спецификацию достигается за счет того, что операции представляются как часть типа.

Для реализации АТД необходимо, во-первых, выбрать представление памяти для объектов и, во-вторых, реализовать операции в терминах выбранного представления.

Примером абстрактного типа данных является класс в языке С++.

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

Возможность использовать знаки стандартных операций для записи выражений как для встроенных, так и для АТД.

В языке С++ для перегрузки операций используется ключевое слово operator, с помощью которого определяется специальная операция-функция (operator function).

Формат операции-функции:

тип_возвр_значения operator знак_операции (специф_параметров)

{операторы_тела_функции}

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

Любая унарная операция Å может быть определена двумя способами: либо как компонентная функция без параметров, либо как глобаль-

 

ная (возможно дружественная) функция с одним параметром. В первом случае выражение Å Z означает вызов Z.operator Å (), во втором - вызов operator Å(Z).

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

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

Синтаксис:

а) в первом случае (описание в области класса):

тип_возвр_значения operator знак_операции

б) во втором случае (описание вне области класса):

тип_возвр_значения operator знак_операции(идентификатор_типа)

 

Примеры.

1) class person                                   2) class person

{ int age;                                             { int age;

...                                                        ...

public:                                                   public:

…                                                      ...

void operator++(){ ++age;}             friend void operator++(person&);

};                                                        };

void main()                                   void person::operator++(person& ob)

{class person jon;                               {++ob.age;}

++jon;}                                              void main()

                                                            {class person jon;

                                                            ++jon;}

 

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

· Любая бинарная операция Å может быть определена двумя способами: либо как компонентная функция с одним параметром, либо как глобальная (возможно дружественная) функция с двумя параметрами. В первом случае x Å y означает вызов x.operator Å (y), во втором – вызов operator Å (x,y).

 

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

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

Примеры.

1) class person{…};

class adresbook

{ // содержит в качестве компонентных данных множество объектов типа       //person, представляемых как динамический массив, список или дерево

public:

person& operator[](int); //доступ к i-му объекту

};

person& adresbook:: operator[](int i){...}

void main()

{class adresbook persons;

class person record;

record = persons [3];

}

 

2) class person{…};

class adresbook

{ // содержит в качестве компонентных данных множество объектов типа//person, представляемых как динамический массив, список или дерево

public:

friend person& operator[](const adresbook&,int);//доступ к i-му объекту

};

 person& operator[](const adresbook& ob,int i){...}

void main()

{class adresbook persons;

class person record;

record = persons [3];

}

 

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

Операция отличается тремя особенностями:

· операция не наследуется;

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

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

Формат перегруженной операции присваивания:

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

Отметим две важные особенности функции operanor=. Во-первых, в ней используется параметр-ссылка. Это необходимо для предотвращения создания копии объекта, передаваемого через параметр по значению. В случаи создания копии, она удаляется вызовом деструктора при завершении работы функции. Но деструктор освобождает распределенную память, еще необходимую объекту, который является аргументом. Параметр-ссылка помогает решить эту проблему.

Во-вторых, функция operator=() возвращает не объект, а ссылку на него. Смысл этого тот же, что и при использовании параметра-ссылки. Функция возвращает временный объект, который удаляется после завершения ее работы. Это



Поделиться:


Последнее изменение этой страницы: 2021-11-27; просмотров: 293; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.15.237.255 (0.292 с.)