close

Вход

Забыли?

вход по аккаунту

Low-res 5 mb;pdf

код для вставкиСкачать
ТЕХНОЛОГИЯ ПРОГРАММИРОВАНИЯ
Методические указания к лабораторным работам
НОВОСИБИРСК
2014
ТЕХНОЛОГИЯ ПРОГРАММИРОВАНИЯ
Методические указания к лабораторным работам
для студентов II курса АВТФ
(направления 550200 и 552800)
дневной и заочной формы обучения
НОВОСИБИРСК
2014
Составитель: Д.О. Романников, канд. техн. наук, ст. преп.
Рецензент А.В. Гунько, канд. техн. наук, доц. каф. автоматики
Работа подготовлена на кафедре автоматики
© Новосибирский государственный технический университет, 2014 г.
2
Лабораторная работа №1
Цель работы: изучить основы работы с классами в С++, а именно: структуру классов,
спецификаторы области видимости, реализацию методов, в том числе конструкторов и
деструкторов.
Общие сведения
C++ — компилируемый статически типизированный язык программирования общего назначения.
Является вторым по распространению языком в мире после Java. C++ используется не только в
персональных и серверных компьютерах, но и широко распространен для программирования
микроконтроллеров и DSP процессоров.
Hello world на С++
Пожалуй, самой распространенной программой для начала изучения языка программирования
является программа “Hello world”. Рассмотрим программу, приведенную ниже:
#include <iostream>
using namespace std;
void main()
{
cout << "Hello world!" << endl;
cout << "Welcome to C++ programming" << endl;
return 0;
}
Данная программа выводит на консоль строки “Hello world” и “Welcome to C++ Programming”.
Реализуется при помощи выражения “cout <<”, которое используется для вывода на поток stdout
выражения стоящего правее. Для работы с "cout" необходимо подключить библиотеку
"<iostream>" и указать, что будет использоваться пространство имен (namespace) “std”.
Классы
В С++ для программирования в объектно-ориентированном стиле используются классы и объекты.
Пример объявления класса приведен ниже:
#include <iostream>
using namespace std;
class Line
{
private:
double length;
3
public:
void set_length(double);
double get_length(void);
line();
};
Необходимо отметить, что хорошей практикой является разделение объявления класса и его
реализации по отдельным файлам. Например, вышеприведенный код необходимо разместить в
файле с названием класса – “line“ и расширением “h“ или “hpp“ (line.h/line.hpp). А
реализацию методов класса разместить отдельно в файле «line.cpp». Рассмотрим эту
реализацию:
#include “line.h”
using namespace std;
void Line::set_length( double len )
{
length = len;
}
double Line::get_length( void )
{
return length;
}
Методы класса имеют доступ к атрибутам класса: могут получать значение переменных и
изменять его. Пример использования реализованного класса приведен ниже:
int main( )
{
Line new_line;
// set line length
new_line.set_length(6.0);
cout << "Length of line : " << new_line.get_length();
cout << endl;
return 0;
}
Классы в С++ могут иметь различную область видимости, а именно:

private – атрибуты или методы доступны только «внутри» класса;
4

protected – атрибуты или методы доступны «внутри» класса, а также классам потомкам;

public – атрибуты или методы доступны только как «внутри» и «снаружи» класса, так и для
потомков;
Работа с памятью в С++
С++ как язык, не содержащий «сборщика мусора», предполагает ручное управление памятью. Для
выделения памяти используется оператор new, а для ее освобождения оператор delete.
Синтаксис выделения и освобождения памяти с использованием операторов new и delete
приведен ниже:
p_var = new typename;
delete p_var
Где p_var – это указатель типа typename. Рассмотрим пример использования операторов
new/delete для классов:
#include <iostream>
#include “line.h”
using namespace std;
int main()
{
Line *new_line = new Line;
// set line length
new_line->set_length(6.0);
cout << "Length of line : " << new_line->get_length();
cout << endl;
delete new_line;
return 0;
}
Конструкторы, деструкторы
При программировании в ООП стиле часто бывает необходимым выполнить начальную
инициализацию объекта, например: выделить память под атрибуты класса, создать файл или
выполнить запрос в базу данных. Для этих целей используются конструкторы. Деструктор
имеет обратное назначение. Данные методы подчиняются следующим правилам:



Конструктор имеет такое же имя, как и класс;
Конструктор не имеет возвращаемого значения;
Когда в программе создается экземпляр класса, компилятор вызывает конструктор, если
конструктор существует;
5


Деструктор имеет такое же имя, как и класс, но содержит тильду «~» перед именем;
Деструктор не имеет возвращаемого значения.
Рассмотри пример реализации конструктора:
#include <iostream>
using namespace std;
class Line
{
private:
double *length;
public:
line(int);
~line();
void set_length(double);
double get_length(void);
line();
};
Line::line(void)
{
length = new double;
}
Line::~line(void)
{
delete length;
}
void Line::set_length( double len )
{
*length = len;
}
double Line::get_length( void )
{
return *length;
}
int main()
{
Line *new_line = new Line;
6
// set line length
new_line->set_length(6.0);
cout << "Length of line : " << new_line->get_length();
cout << endl;
delete new_line;
return 0;
}
Порядок выполнения работы
1. Реализовать функции программы согласно варианту. Сделать атрибуты класса
приватными. Добавить в класс protected методы, для валидации входных данных в
методах. Общий код вынести в отдельные методы;
2. Запретить всем методам (для которых это возможно по смыслу) менять состояние класса;
3. Написать unit-тесты.
Методические указания
1. Для работы с входными параметрами argv реализуйте отдельный класс;
2. Отчет должен содержать примеры запуска программы и результаты запуска unit-тестов.
Контрольные вопросы
1.
2.
3.
4.
5.
Для чего нужны классы?
Какими способами выполняется обращение к методам/атрибутам объекта?
Какие бывают спецификаторы областей видимости, чем они отличаются?
Как в С++ выполняется вывод на stdout/stderr?
Возможно ли, чтобы у класса было несколько конструкторов? Для чего применяется? Как
«программа определяет» какой конструктор необходимо вызвать?
6. При каких условиях вызывается деструктор класса? Примеры.
7. Когда нужно выделять память под переменные статически, когда динамически?
Лабораторная работа №2
Цель работы: изучить основные принципы наследования классов в С++.
Общие сведения
Наследование – механизм, позволяющий написать новый класс (дочерний, производный) на
основе уже существующего класса (родительского, базового). При этом поведение дочернего
класса определяется методами и атрибутами, которые получены от класса-родителя, а также
новыми методами и атрибутами дочернего класса.
Рассмотрим пример наследования классов.
class Parent {};
class Child_1: public Parent {};
7
class Child_2: protected Parent {};
class Child_3: private Parent {};
В данном примере от базового класса Parent наследуются классы Child_1, Child_2, Child_3, причет
все наследуемые классы имеют различные спецификаторы наследования: public, protected,
private. Public-наследование означает, что те методы и атрибуты базового класса, которые были
объявлены как public и protected остаются public и protected после наследования соответственно.
Protected-наследование означает, что те методы и атрибуты базового класса, которые были
объявлены как public и protected будут иметь спецификатор доступа protected после
наследования. Private-наследование означает, что те методы и атрибуты базового класса, которые
были объявлены как public и protected будут иметь спецификатор доступа private после
наследования.
Стоит заметить, что не все методы и атрибуты класса наследуются. Наследованию не подлежат
все конструкторы, деструкторы класса, методы и атрибуты, которые имеют спецификатор private.
Конструкторы
Конструкторы при наследовании классов не наследуются. Также объявление конструктора в
классе-потомке выглядит иначе. Рассмотрим пример.
class Parent
{
int x, y;
public:
Parent(int a, int b);
};
Parent::Parent(int x, int y) {
this->x = x;
this->y = y;
}
class Child: public Parent
{
int z;
public:
Child(int x, int y, int z);
};
Child::Child(int x, int y, int z): Parent(x, y)
{
8
this->z = z;
}
Обратите внимание на то, каким образом объявлен конструктор для класса Child: после обычного
объявления конструктора следует вызов конструктора родительского класса с аргументами из
конструктора класса-наследника.
Порядок вызова конструкторов/деструкторов
При наследовании важное значение имеет то, в каком порядке будут вызываться
конструкторы/деструкторы
родительского
и
дочернего
классов.
Порядок
вызова
конструкторов/деструкторов следующий: при создании объекта дочернего класса конструкторы
вызываются в порядке от родительского класса к классу-наследнику. Деструкторы вызываются
строго в противоположном порядке – от дочернего класса к родительскому.
Переопределение методов
Цель наследования классов – изменить поведение класса потомка на основании поведения
класса родителя. Для этого можно добавлять новые методы (чаще всего с использованием
методов родительского класса) или переопределить поведение существующих.
Порядок выполнения работы
1. Реализовать функции программы согласно варианту. Общий код необходимо вынести в
отдельные методы;
2. Написать unit-тесты.
Методические указания
1. Добавить в программу новые сущности согласно варианту с использованием механизма
наследования (где это необходимо);
2. Отчет должен содержать примеры запуска программы и результаты запуска unit-тестов.
Контрольные вопросы
1.
2.
3.
4.
5.
Для чего нужно наследование?
Как работает наследование в С++? Какие области видимости наследуются?
Почему в С++ принято определять виртуальный деструктор?
Наследуются ли конструкторы/деструкторы классов?
Каким образом можно из класса потомка обратиться к методам/атрибутам родительского
класса?
6. Как происходит вызов конструкторов в цепочке классов наследников?
Лабораторная работа №3
Цель работы: изучить основные принципы работы с контейнерами STL и их итераторами в С++.
Общие сведения
В стандартной библиотеке (STL) C++ для работы с распространенными структурами данных есть
множество реализованных классов для упрощения работы с ними. Основными контейнерами
являются: vector, map, list, bitset, queue, stack.
9
Для работы с массивами в С++ используется специальный контейнер vector. Создание вектора
выполняется следующим образом: «vector<type> variable_name;», где type – любой
валидный тип данных, а variable_name – имя контейнера. Основными методами для работы с
векторами являются:
v.push_back(100); // Добавление нового значения (100) в конец массива
v.pop_back(); // Извлечение из массива последнего значения
v.at(10) = 5; //v[10] = 5; Обращение к 10-ому элементу массива
size_t size = v.size(); // Получение размера массива
bool v.empty(); // Проверка на то, что массив пуст
v.clear(); // Удаляет все элементы массива
Контейнер list также предназначен для работы с массивами, но в отличие от вектора, в котором
элементы массива хранятся в непрерывной последовательной области памяти, элементы массива
хранятся в произвольных ячейках памяти, что приводит к более медленной сортировке и более
быстрому добавлению/удалению в произвольном месте массива.
Создание списка выполняется так же, как и для вектора, за исключением использования
ключевого слова list: «list<type> variable_name;».
Другим важным контейнером для ассоциации по типу ключ-значение, является контейнер map.
Для создания контейнера необходимо подключить заголовочный файл <map> и написать «map
<key type,data type> variable _name;», где key_type и data_type тип данных ключа и значения
соответственно. Для доступа к контейнеру можно использовать запись: variable_name[key].
Основными методами для работы с контейнером являются:
bool m.empty(); // // Проверка на то, что контейнер пуст
m.count(key); // Возвращает 1 если элемент с ключом key существует, иначе 0
m.find(key); // Возвращает итератор на элемент с указанным ключом
m.erase(it); // Удаляет элемент с заданным итератором
m.size(); // Получение размера
m.clear(); // Удаляет все элементы массива
Итератор (iterator) – специальный тип данных, предназначенный для универсального перебора
элементов контейнеров.
Во всех контейнерах С++ есть методы begin() и end(), возвращающие итераторы для
соответствующих контейнеров. Перебор элементов c использованием итераторов будет
выглядеть следующим образом:
vector<int> myvector;
for (vector<int>::iterator it = myvector.begin(); it != myvector.end(); ++it)
cout << *it << endl;
10
Порядок выполнения работы
1. Реализовать функции программы согласно варианту. Общий код необходимо вынести в
отдельные методы;
2. Написать unit-тесты.
Методические указания
1. Для реализации сортировки используйте подходящий контейнер;
2. При использовании С++0х11 удобно создавать итераторы при помощи ключевого слова
auto: auto it = myvector.begin();
3. Отчет должен содержать примеры запуска программы и результаты запуска unit-тестов.
Контрольные вопросы
1.
2.
3.
4.
Какие есть контейнеры в С++?
Что такое итераторы? Для чего они используются?
Как итерироваться по list, map, vector?
В чем отличие между stack/queue, list/vector?
Лабораторная работа №4
Цель работы: изучить способы переопределения операторов класса и их использование.
Общие сведения
Язык С++ позволяет выполнить перегрузку операторов, что по сути является более удобным
способов вызова методов классов. Перегрузка операторов должна использоваться для упрощения
чтения и написание кода. Рассмотрим примеры перегрузки операторов.
Унарный оператор.
По синтаксису перегрузка операторов напоминает перегрузку методов с использованием
ключевого слова operator. Например:
class CodeBlock {
int value;
public:
CodeBlock& operator++(CodeBlock& c); // префиксный оператор
CodeBlock& operator++(CodeBlock& c, int); // постфиксный оператор
};
CodeBlock& operator++(CodeBlock& c) {
c.value++;
return c;
}
CodeBlock& operator++(CodeBlock& c, int) {
11
CodeBlock before_add(c);
c.value++;
return before_add;
}
В рассмотренном выше примере переопределяется унарный оператор «++». Стоит заметить, что в
примере показано переопределение оператора в случае его префиксного использования (++с) и
постфиксного использования (с++).
Рассмотрим пример переопределения бинарного оператора сравнения «==»:
class CodeBlock {
int value;
public:
bool operator==(CodeBlock& left, CodeBlock& right);
};
bool operator==(CodeBlock& left, CodeBlock& right) {
return left.value == right.value;
}
В отличие от предыдущего примера, при перегрузке бинарного оператора необходимо передать
оба аргумента, стоящие слева и справа в выражении.
Стоит заметить, что не все операторы можно перегрузить.
Порядок выполнения работы
1. Необходимо реализовать функции поиска/фильтрации в программе согласно варианту;
2. Напишите unit-тесты для реализованных функций.
Методические указания
1. Для
реализации
функций
поиска/фильтрации
необходимо
использовать
переопределенные операторы;
2. Создайте не менее 5 различных фильтров по различным атрибутам;
3. Отчет должен содержать примеры запуска программы, а также результаты запуска unitтестов.
Контрольные вопросы
1.
2.
3.
4.
Для чего необходимо выполнять перегрузку операторов?
Какие операторы можно/нельзя переопределять?
Каким образом различается переопределение префиксных/постфиксных операторов?
Как выполняются операции преобразования типов?
12
Лабораторная работа №5
Цель работы: изучить работу с потоками.
Общие сведения
В С++ используются потоки (stream, не путать с потоками операционной системы) для работы с
операциями ввода/вывода на консоль или файлы. Концепция потоков широко используются в С++
из-за того, что она обладает следующими достоинствами:



универсальность. Все классы потоков реализуют один интерфейс, поэтому разработчику
нет необходимости заботиться о типе потока;
масштабируемость. Разработчик может легко добавить методы работы с потоками для
своих объектов;
абстракция. Разработчику нет необходимости указывать тип вводимого/выводимого
объектов.
Для работы с потоками в С++ используется иерархия классов (рис. 1).
cin
ios_base
ios
ostream
istream
iostream
streambuf
fstream
fstream
stringstream
stringstream
filebuf
stringbuf
cout, cerr, clog
ifstream
istringstream
Рис. 1. Иерархия классов для работы с потоками в С++
Основными типами потоков являются: потоки для работы с вводом/выводом на консоль
(<ostream>, <istream>, <iostream>) и глобальные объекты (cin, cout, cerr, clog), через которые
происходит доступ к потокам, потоки для работы с вводом/выводом в файл (<filebuf>), потоки для
форматирования строк (<stringsteam>, <istringsteam>, <ostringsteam>).
Для операции записи в поток используется оператор «<<»: cout << “My dummy string”;. Для
операции чтения из потока используется оператор «>>»: cin >> “Input any value: ”;.
Для форматирования строк в С++ применяется класс stringstream. Пример применения
рассмотрен ниже:
#include <sstream>
#include <iostream>
int main () {
std::stringstream ss; int v = 44;
std::cout << “The value of v variable is: \”” << v << “\”” << std:endl;
13
ss << v;
std::string s = ss.str();
}
Базовый класс ios предоставляет методы для определения состояния потока: bool good() –
сигнализирует, что ни один из битов eofbit, failbit, badbit не взведен; bool eof() – сигнализирует о
том, что «End-of-File» достигнут для последовательности данных ассоциированных с потоком; bool
fail() – возвращает true, если один из failbit, badbit битов взведен; bool bad() – возвращает true,
если взведен badbit бит, означающий потерю целостности потока. Также получить значение битов
состояний можно при помощи метода iostate rdstate().
Для работы с файлами также используются потоки. Рассмотрим пример:
#include <iostream>
#include <fstream>
int main () {
std::fstream fs;
fs.open ("test.txt");
if (fs.is_open()) {
fs << "lorem ipsum";
std::cout << "Operation successfully performed\n";
fs.close();
} else {
std::cout << "Error opening file";
}
return 0;
}
В данном примере для открытия файла используется метод void open (const char* filename,
ios_base::openmode mode = ios_base::in | ios_base::out). Для проверки открытия файла метод bool
is_open() и для закрытия – метод void close().
Важной частью потоков в С++ является переопределение операций чтения/записи для объектов.
Рассмотрим пример класса с переопределенными операторами.
class Pair {
int first, second;
public:
14
ostream& operator<<( ostream &output, const Pair &p ) {
output << "(" << p.first << ", " << p.second << ")" << endl;
return output;
}
istream& operator>>( istream &input, Pair &p ) {
input >> p.first >> p.second;
return input;
}
};
Порядок выполнения работы
1. Необходимо реализовать функции ввода/вывода сущностей из потока в программе
согласно варианту;
2. Напишите unit-тесты для реализованных функций.
Методические указания
1. Для реализации ввода сущности в поток используйте последовательность символов с
разделителями так, чтобы было возможно вводить несколько сущностей за «один такт»;
2. Для перенаправления потока ввода в запускаемую программу можно использовать
следующую
команду
для
Windows:
и
для
Linux:
./program
<
“sample_string;10;[email protected]_again;30;40”;
3. Отчет должен содержать примеры запуска программы, а также результаты запуска unitтестов.
Контрольные вопросы
1.
2.
3.
4.
5.
Как выполняется перегрузка операторов ввода/вывода?
Модификаторы открытия файла.
Как определить пригодность потока к работе/ошибки/другие состояния?
Файловые потоки.
Как можно итерироваться по потоку?
Лабораторная работа №6
Цель работы: изучить работу с исключениями.
Общие сведения
Исключения – механизм языков программирования, предназначенный для описания реакции
программы на различные ошибки и/или проблемы (исключения). Также исключения позволяют
значительно упросить затраты на обработку ошибок. Рассмотрим пример обработки исключений.
try {
throw 1;
15
} catch (int a) {
cout << "Exception handler: " << a << endl;
return;
}
cout << "No exception detected!" << endl;
В вышеприведенном примере используются следующие ключевые слова: try, throw, catch.
Конструкция throw используется для «кидания» исключения, где объектом исключения является
аргумент. Конструкция catch (int a)… является «ловушкой» для всех кидаемых исключений с типом
int, которые «кидаются» в конструкции try. При перехвате исключения управление в программе
передается в catch блок.
В стандартной библиотеке С++ используется иерархия классов исключений, где базовым классом
является exception, которой содержит метод virtual const char* what() const noexcept для описания
исключительных ситуаций.
Если в блоке try «кидается» несколько различных типов исключений, то можно использовать
несколько catch конструкций, например:
try {
…
} catch (int a) {
cout << "Exception handler: " << a << endl;
} catch (exception& e) {
cout << "Exception handler: " << e.what() << endl;
} catch (…) {
cout << "Catch all!" << endl;
}
В вышеприведенном примере catch(…) используется для перехвата всех исключений.
Порядок выполнения работы
1. Реализовать обработку ошибок в программе при помощи исключений;
Методические указания
1. В качестве объектов исключений используйте иерархию классов исключений;
2. Для различных по смыслу ошибок используйте различные классы исключений;
3. В базовом классе исключений реализуйте виртуальную функцию, в которой будет
выводиться информация о классе объекта, файле и номера строки.
Для вывода номера строки и файла используйте встроенные макросы __LINE__ и __FILE__;
4. Отчет должен содержать примеры запуска программы и результаты запуска unit-тестов.
16
Контрольные вопросы
1.
2.
3.
4.
Для чего необходимы исключения в С++?
Каким образом перехватываются исключения в С++?
Иерархия стандартных исключений в STL.
Каким образом выполняется освобождение памяти при возникновении исключительных
ситуаций?
Варианты заданий
Результатом выполнения лабораторных работ по курсу «Технология программирования» является
программа согласно вариантам приведенным ниже. Каждая из лабораторных работ будет
добавлять функциональность в программу.
В.1. Разработка CLI-утилиты для управления задачами. В ходе лабораторных работ
последовательно необходимо реализовать следующее:
ЛР.1. Управление списком задач: создание/удаление/редактирование;
ЛР.2. Управление
списком
пользователей
и
дефектами:
создание/удаление/редактирование,
назначение
пользователя
на
задачу,
создание/удаление дефекта к задаче;
ЛР.3. Реализуйте сортировку задач/дефектов по различным параметрам;
ЛР.4. Создать фильтры по заданным параметрам для поиска дефектов и/или задач;
ЛР.5. Создание сущностей из потока и вывод их в поток;
ЛР.6. Реализовать иерархию классов исключений и выполнить рефакторинг для
внедрения контроля ошибок с использованием исключений.
В.2. Разработка CLI-утилиты для различных типов вычислений. В ходе лабораторных работ
последовательно необходимо реализовать следующее:
ЛР.1. Выполнение алгебраических и логических операций умножения/деления/
сложения/вычитания;
ЛР.2. Выполнение матричные операции сложения/вычитания;
ЛР.3. Реализуйте историю выполненных вычислений и ее отображение;
ЛР.4. Реализуйте функцию запоминания результата и извлечение запомненого
результата;
ЛР.5. Выполнение операций из потока и вывод результатов в поток;
ЛР.6. Реализовать иерархию классов исключений и выполнить рефакторинг для
внедрения контроля ошибок с использованием исключений.
В.3. Разработка CLI-утилиты для выполнения файловых операций. В ходе лабораторных работ
последовательно необходимо реализовать следующее:
ЛР.1. Выполнение создания/копирование/переименования/удаления файлов;
ЛР.2. Выполнение создания/копирование/переименования/удаления директорий;
ЛР.3. Реализуйте вывод на экран отсортированного списка файлов согласно их именам;
ЛР.4. Поиск по имени файла/директории по заданному пути;
ЛР.5. Создание сущностей из потока и вывод результатов в поток;
ЛР.6. Реализовать иерархию классов исключений и выполнить рефакторинг для
внедрения контроля ошибок с использованием исключений.
17
В.4. Разработка CLI-утилиты для работы с датами. В ходе лабораторных работ последовательно
необходимо реализовать следующее:
ЛР.1. Реализовать подсчет количества дней/месяцев/лет между датами;
ЛР.2. Реализовать отображение календаря на дисплей с разбиением на месяцы;
ЛР.3. Реализуйте историю выполненных вычислений и ее отображение;
ЛР.4. Реализовать преобразование между датой в формате DD.MM.YYYY в UNIX
timestamp и обратно;
ЛР.5. Выполнение операций из потока и вывод результатов в поток;
ЛР.6. Реализовать иерархию классов исключений и выполнить рефакторинг для
внедрения контроля ошибок с использованием исключений.
В.5. Разработка CLI-утилиты для поиска слов в файле. В ходе лабораторных работ
последовательно необходимо реализовать следующее:
ЛР.1. Реализовать поиск заданного текстового фрагмента в заданном файле;
ЛР.2. Реализовать поиск заданного текстового фрагмента во всех файлах директории;
ЛР.3. Реализуйте вывод на экран всех слов в файле в порядке их наибольшего
вхождения;
ЛР.4. Реализуйте поиск заданного текстового фрагмента в файле с конца файла;
ЛР.5. Реализовать чтение заданного текстового фрагмента и списка файлов/директории
из потока и вывод найденных строк в поток;
ЛР.6. Реализовать иерархию классов исключений и выполнить рефакторинг для
внедрения контроля ошибок с использованием исключений.
В.6. Разработка CLI-утилиты для работы с рациональными числами (m/n, где n,m Є Z). В ходе
лабораторных работ последовательно необходимо реализовать следующее:
ЛР.1. Выполнение операций умножения/деления/ сложения/вычитания на целое число;
ЛР.2. Выполнение операций умножения/деления/ сложения/вычитания между
рациональными числами;
ЛР.3. Реализуйте историю выполненных вычислений и ее отображение;
ЛР.4. Реализуйте сложение/вычитание векторов рациональных чисел;
ЛР.5. Выполнение операций из потока и вывод результатов в поток;
ЛР.6. Реализовать иерархию классов исключений и выполнить рефакторинг для
внедрения контроля ошибок с использованием исключений.
18
1/--страниц
Пожаловаться на содержимое документа