Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ЛаборООП.docx
Скачиваний:
49
Добавлен:
01.06.2015
Размер:
1.46 Mб
Скачать

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

Варианты заданий приведены в следующей таблице:

T1 T2

  1. ТреугольникКвадрат

  2. ТреугольникПрямоугольник

  3. Треугольник Параллелограмм

  4. Треугольник Трапеция

  5. Треугольник Шестиугольник

  6. Треугольник Восьмиугольник

  7. Квадрат Прямоугольник

  8. Квадрат Параллелограмм

  9. Квадрат Трапеция

  10. Квадрат Шестиугольник

  11. Квадрат Восьмиугольник

  12. Прямоугольник Параллелограмм

  13. Прямоугольник Трапеция

  14. Прямоугольник Шестиугольник

  15. Прямоугольник Восьмиугольник

  16. Параллелограмм Трапеция

  17. Параллелограмм Шестиугольник

  18. Параллелограмм Восьмиугольник

  19. Трапеция Шестиугольник

  20. Трапеция Восьмиугольник

  21. Шестиугольник Восьмиугольник

Контрольные вопросы

  1. Различают 4 элемента определения функции: тип, имя, список параметров, тело. Какими элементами могут отличаться экземпляры одной виртуальной функции, находящиеся в разных производных классах?

  2. Что возвращает switch в листинге 3.1 при case 3?

  3. В каких случаях применяется фабрика объектов?

  4. Что изменится в поведении программы 3.2, если в базовом классе лишить метод GetArea атрибута virtual?

  5. Можно ли в программе 3.2 в каком-либо производном классе не определять метод CreateShape?

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

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

class Based {

public: virtual void speak() {cout<<“Hello, Based”<<endl;}

}; //Based

class Derived: public Based {

public: void speak(){cout<<“Hello, Derived”<<endl;}

}; //Derived

void print ( Based* p){p->speak();}

void main()

{ ?? ?d1;

?d1=new ??;

print (?d1);

delete d1;

}

8. Как изменятся операторы в функции main, если после выполнения предыдущего задания изменить определение функции print на следующее

void print ( Based& p){p.speak();}

Лабораторная работа 4. КонтейнерыStl. Обработка исключений Цель работы.

Цель работы – ознакомление с классами-контейнерами библиотеки STL, файловой системой и обработкой исключений.

Основные сведения

Контейнеры.

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

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

Различают последовательные и ассоциативные классы- контейнеры. В данной работе используются только первые.

Среди последовательных - vector(вектор), list(список), deque(двусторонняя очередь) – наиболее распространенным является vector. Отличительной особенностью vector, как и всех контейнеров, является возможность увеличения количества элементов, хранящихся в контейнере, в процессе выполнения программы c помощью методов соответствующего класса. На его основе можно конструировать динамические массивы элементов различных типов, в том числе и определенных пользователем.

Для добавления в конец и удаления последнего элемента контейнера любого из трех рассматриваемых классов предназначены методы push_back() и pop_back(). Kpоме того, список и очередь (но не вектор) поддерживают операции вставки и удаления первого элемента контейнера push_front() и pop_front().

vector<double> vd; //Пустойконтейнер

vd.push_back(45.56);

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

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

vector<int> v(20); // начальный размер контейнера – 20 элементов.

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

v[2]=3;

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

Размер контейнера может быть изменен при работе программы функцией resize(N).

vector <char> vc;

vc.resize(34); // Размер контейнера 34

vc[6]=’d’;

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

vector <int> vv1;

vector<int> ::iterator iv1; //Итератор

Метод begin возвращает значение итератора – указателя на начало контейнера, метод end – на конец.

В библиотеке STL определено большое количество т.н. алгоритмами (algorithm) – функций обработки элементов контейнеров: поиска(find), сортировки(sort) и т.п. При использовании алгоритмов могут использоваться функциональные объекты [1]. Следует отметить, что алгоритмы не являются методами классов-контейнеров и могут быть использованы для работы с обычными массивами (см. замечание к листингу 1.1).

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

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

Работа с файлами

Для работы с дисковыми файлами в языке С++ предусмотрен класс fstream и производные от него классы ifstream и ofstream. Для записи или считывания данных надо объявить объект соответствующего класса и связать его с файлом.

Например,

ifstream fin ( имяфайла, режим_чтения);

ofstream fout(( имяфайла, режим_записи);

В классе fstream для файловых объектов определены операции вставки и извлечения, так что для стандартных типов и строкового типа string можно применять привычную запись типа fin>> …, fout<<…. Для пользовательских типов операции надо переопределять (см. пример программы ниже).

Двухаргументные методы read и write позволяют считывать/записывать в файл блоки двоичных данных. Количество байт в блоке является аргументом этих методов. Считываются и записываются символьные массивы типа char*; числовые типы следует преобразовывать, например, операцией reinterpret_cast<char*>. Операция преобразования позволяет сохранять в файле объект целиком, а не по компонентам.

Обработка исключений

Под исключением понимается некоторая ситуация, возникающая при выполнении программы, вследствие появления которой нормальное продолжение программы невозможно. Дальнейшее выполнение программы определяется программистом на этапе проектирования и носит название обработки исключения. Прерывание программы пользователем или от аппаратуры ( нажатие клавиш Ctrl-Break, сигнал таймера) как исключения не рассматриваются.

В С++ введен специальный механизм обработки исключений. Он организуется при помощи трех ключевых слов: try, catch и trow.

В блок try помещается или вся программа, или фрагмент ее, в котором может возникнуть исключительная ситуация. При ее возникновении возбуждается (throw) исключение, которое перехватывается и обрабатывается в блоке catch, тип аргумента которого совпадает c типом выражения в операторе throw.

Схема размещения такова:

try { …

throw выражение1;

….

throw выражение2;

…..

}

catch (тип арг1)

{ ….}

catch (тип арг2)

{ ….}

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

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

Примеры организации исключений.

void main()

{

try {……..

…. throw 1;

……..

…. throw ‘a’;

}//try

catch (int){…….}

catch (char) {…..}

}//main

Операторы throw могут быть включены в функцию, а вызов ее помещен в блок try

void MyFunc(){

……..

if(…)throw 1;

……..}//MyFunc

void main()

{try

{ MyFunc(); ……

}//try

catch(int){……}

}//main

Наконец, блоки try и catch могут быть включены в функцию.

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

Например,

class ReadError {

public: ReadError () {cout << “Error Read”;}

};

class WritedError {

public: WriteError () {cout << “Error Write”;}

};

void main ()

{ try{

ifstream in(“имя”);

if(!in) throw ReadError ();

….

ofsteam out (“имя”);

if(!out) throw WriteError();

….

}//try

catch(ReadError){…}

catch(WriteError){….}

}//main

Классы исключений можно объединять в иерархию. Например, класс арифметических ошибок, класс ошибок ввода данных и др. могут быть производными классами некоторого базового класса исключений. (См. контрольные вопросы).

Ниже приводится пример программы с использование контейнера типа vector c объектами класса MyRecord.

листинг 4.1

#include "stdafx.h"

#include <iostream>

#include <fstream>

#include <vector>

#include <string>

#include <ctype.h>

#include <algorithm>

using namespace std;

class MyRecord; // Объявление класса

typedef vector <MyRecord> Base; // Переименование типа - необязательно

vector<MyRecord>::iterator index ; //Определение итератора index

class MyRecord{

string Name;

int age;

public:

/* Здесь можно включить проверку правильности ввода данных,

например, в name не должны вводиться цифры, возраст не может превышать 100 и т.п.*/

void InputRecord()

{

string name;

int len;

cout<<"Input Name & Age"<<endl;

cin>>name;

len=name.length();

for(int i=0; i<len; i++)

if(isdigit(name[i])) throw 1;//isdigit в <сtype.h>

Name=name;

cin>>age;

}

void ShowRecord() const { cout<<Name<<" "<<age<<endl;}

int GetAge()const {return age;}

string GetName()const {return Name;}

/* На всякий случай - перегрузка вставки и извлечения для файловых

объектов и класса MyRecord*/

friend ifstream& operator >> (ifstream& s, MyRecord& obj)

{ s>>obj.Name>>obj.age; return s;}

friend ofstream& operator << (ofstream& s, MyRecord& obj)

{ s<<obj.Name<<endl<<obj.age<<endl; return s;}

/*Перегрузка операции < для применения алгоритма сортировки из библиотеки

algorithm; сортировка ведется по полю Name*/

bool operator<( MyRecord b)

{

if(Name<b.Name) return true;

return false;

}

};

class MyBase{

Base bs;

MyRecordmr;

public:

//ввод элементов в контейнер

void InputBase()

{

char flag=true;

while (flag)

{

try

{ mr.InputRecord();

bs.push_back(mr);

flag=false;

}//try

catch(int) { cout<<"Once more Name"<<endl; }

}

}

void ShowBase()

{ int n=bs.size();

for (int i=0; i<n; i++)

bs[i].ShowRecord();

}

//Перегрузка индексации для MyBase

MyRecord& operator [](int i)

{return bs[i]; }

//Очистка контейнера

void ClearBase()

{bs.clear(); }

//Вычисление среднего значения элементов age в контейнере

int AverageAge()

{

int n=bs.size();

int buf=0;

for(index=bs.begin(); index!=bs.end(); index++)

buf=buf + (*index).GetAge();

buf=buf/n;

return buf;

}

//Сортировка

void SortBase()

{ sort(bs.begin(),bs.end());}

//Запись в файл содержимого контейнера

void InFile()

{//Здесь включить tryна открытие файла

fstream out("c:\\Base.txt",ios::out);

int n=bs.size();

for (int i=0; i<n; i++)

out<<bs[i].GetAge()<<" "<<bs[i].GetName();

}

};

int main()

{

MyBase mb;

mb.InputBase();

mb.ShowBase();

mb.SortBase();

mb.InFile();

mb.ClearBase();

mb.ShowBase();

return 0;

}

Замечания

1. Класс Base служит фасадом. В нем инкапсулированы все методы работы с контейнером, благодаря чему main выглядит очень незатейливо.

2. Обратите внимание, что методы ShowBase и AverageAge выполнены по-разному: один с использованием индексации, второй с применением итераторов. В данном случае ( для класса vector) оба метода можно сделать по одному типу – или с индексацией или с итераторами. Но итераторы работают и для других последовательных контейнеров – list и deque, а индексация определена только для vector.

2. В приведенной программе невозможно ввести в контейнер больше одной записи. Исправить это легко – достаточно в main запустить InputBase в цикле. Но делать так не следует, так как этим мы усложним работу клиента. Посмотрите пример из 2-й работы, где массив элементов создается в отдельном классе. Здесь такой класс уже есть и в нем достаточно организовать InputBase такую, что можно было бы добавлять в контейнер несколько элементов без предварительного создания соответствующего массива. Однако, вам придется использовать resize(N) после создания контейнера, вводя значение N по запросу, и комбинировать присваивание и push_back .