МУ_ДЗ_2014 (1079920), страница 4
Текст из файла (страница 4)
cout << "c2 = "<< c2 <<endl;
c2 = a2 - b2;
cout << "c2 = "<< c2 <<endl;
Результат работы программы, полученный на консоли, выглядит так:
c2 = 7
c2 = 2
Нужно знать, что для сложных выражений с операциями, следующую особенность: передача выполняется через стек, который по завершению операции затирается. Если операция планируется для использования в сложных выражениях операции “+” и “–“ нужно описать возврат метода-операции по-другому:
Test & operator +(Test & X )
{
Test *pT = new Test;
pT->Num = this->Num + X.Num ;
return (Test &)*pT;
};
// Или
Test & Test::operator -(Test & X) {
Test * pT = new Test;
pT->Num = this->Num - X.Num ;
return (Test &)*pT;
};
В примере описания операций для промежуточного результата операции выделяется динамическая память. При сложной операции вида:
Test a(3), b(1), c, d(3);
c = a - b + d;
cout << "c = " << c <<endl;
Получим результат в консольном окне:
c = 5
Кроме того. Необходимо учесть, что для сложных объектов, особенно объектов использующих динамическую память, необходимо корректно описать операцию присваивания и конструктор копирования, которые создаются для объектов по-умолчанию и обеспечивают только простое копирования содержание объекта (памяти). Эти вопросы нужно самостоятельно учить в литературе [1].
26 Дружественные функции и классы
Внешние (глобальные) функции называются дружественными (friend), если в теле описания класса описан их прототип со служебным словом friend. Формально это можно записать так:
friend <прототип глобальной функции>;
Глобальная функция должна быть описана после описания класса и доступна в классе. Дружественная функция получает доступ к скрытым членам класса, тех объектов класса, которые ей переданы в качестве параметров или описаны в виде локальных объектов самой функции.
В качестве дружественной функции может выступать и функция член другого класса и весь другой класс в целом (все методы класса дружественные данному классу). В этом случае прототипы членов функций или объявления класса описываются со специальным ключевым словом friend. Пример описания дружественных функций и классов:
class Test {
…
friend void Print(Test &t); // Глобальная дружественная функция
friend class Test2; // Дружественный класс Test2
friend Test1::Change(Test1 t); // Дружественная функция член класса Test1
};
Глобальные дружественные функции могут выполнять перегрузку операций для объектов данного класса. В некоторых случаях такая перегрузка является более удобной и наглядной. Использование механизма дружественности на самом деле является нарушением важного принципа ООП - инкапсуляции, и, по возможности, должно исключаться из программ.
27 Внешние одноместная и двуместная перегрузки операций
Формальное описание внешней функции для перегрузки операций приведено ниже:
<тип возврата> operator <знаки операции>(<список формальных параметров> )
{<тело функции>};
Для примера опишем простейший класс Test1, содержащий прототипы внешних функий для перегрузки операций:
class Test1 {
public:
int Num;
Test1(){ Num = 0; }; // Конструктор без параметров
Test1(int i){ Num = i; }; // Конструктор с параметром
friend Test1 & operator ++(Test1 & T); // Дружественная функция перегрузки
friend Test1 operator +( Test1 & X , Test1 & Y ); // Дружественная функция перегрузки
… };
Число параметров при внешней перегрузке необходимо увеличить на единицу. Для одноместной операции - это один параметр, а для двуместной операции – это два параметра. Для глобальных функций перегрузки операций, после описания класса, необходимо описать сами функции так:
// Внешняя перегруженная операция "+" (двуместная)
Test1 operator+( Test1 & X , Test1 & Y )
{
Test1 *pT = new Test1;
pT->Num = Y.Num + X.Num ;
return *pT;
};
// Внешняя перегруженная операция "++" (одноместная)
Test1 & operator ++(Test1 & T1) {
int h = T1.Num;
h++;
T1.Num = h;
return T1; };
Обратите внимание на то, что в дружественных функциях нет спецификации класса, они не являются методами класса. После описания, в основной программе мы можем проверить использование перегруженной двуместной операции:
Test1 a(5), b(3), c;
c = a + b;
cout << "c = " << c <<endl;
c++;
cout << "c = " << c <<endl;
Получим результат:
c = 8
c = 9
Для корректного использования перегруженных операций в сложных объектах и выражениях необходимо, чтобы в классах были корректно перегружены операция присваивания и конструктор копирования. Эти вопросы нужно самостоятельно учить в литературе [1].
28 Перегрузка операций потоковых операций ввода/вывода
Для примера покажем, как могут быть перегружены операции потокового вывода для пользовательских объектов. Перегрузка потоковых операций выполняется внешней дружественной функцией. Пусть у нас есть класс Test4. Тогда пример класса и перегруженной операции вывод может выглядеть так:
class Test4 {
public:
int Num;
Test4(int i){ Num = i; };
friend ostream & operator << ( ostream & out , Test4 & obj );
friend istream & operator >> ( istream & in , Test4 & obj );
};
Реализация перегруженного метода вывода имеет вид:
ostream & operator <<( ostream & out , Test4 & obj )
{
out << obj.Num ;
return out;
};
Пример использования показан ниже:
Test4 t(5);
cout << "t = " << t << endl;
Получаемый при выполнении этого фрагмента результат приведен ниже:
t = 5
Перегрузка операции ввода выполняется аналогично с помощью внешней дружественной функции:
istream & operator >> ( istream & in , Test4 & obj )
{
cout<< "Введите Num: " ;
in >> obj.Num ;
return in;
};
Пример использования показан ниже:
Test4 t(5);
cin>>t; // Введем - 33
cout << "t = " << t << endl;
Получаемый при выполнении этого фрагмента результат приведен ниже:
Введите Num: 33
t = 33
29 Перегрузка операций индексирования
Для новых пользовательских контейнерных классов можно перегрузить операцию индексирования (“[“, “]”). Пусть мы имеем простейший класс типа массив со следующим описанием:
class Mas {
public:
int Var[20];
Mas( int * pMas , int Razm)
{ for (int i = 0 ; i < Razm ; i++) Var[i] = pMas[i]; };
…
int operator [](int k) { return Var[k]; }; };
В классе предусмотрен конструктор для создания объекта типа целого массива и перегруженный оператор индексирования(“[“, “]”). Теперь, если создать новый объект на основе массива, операцию можно использовать в программах:
// Перегрузка []
int iMas[] = {0,1,2,3,4,5};
Mas NewMas( iMas , sizeof(iMas)/sizeof(int));
cout << "NewMas[4] = " << NewMas[4] << endl;
После выполнения фрагмента получим следующий вывод на консоль:
Введите NewMas[4] = 4
30 Перегрузка операций вызова функции
Для новых пользовательских контейнерных классов можно перегрузить операцию индексирования (“(“, “0”). Пусть мы имеем простейший класс типа массив со следующим описанием:
class Mas {
public:
int Var[20];
Mas( int * pMas , int Razm)
{ for (int i = 0 ; i < Razm ; i++) Var[i] = pMas[i]; };
…
int operator ()(int k) { return Var[k]; }; };
В классе предусмотрен конструктор для создания объекта типа целого массива и перегруженный оператор индексирования(“(“, “)”). Теперь, если создать новый объект на основе массива, операцию можно использовать в программах:
// Перегрузка []
int iMas[] = {0,1,2,3,4,5};
Mas NewMas( iMas , sizeof(iMas)/sizeof(int));
cout << "NewMas(2) = " << NewMas(2) << endl;
После выполнения фрагмента получим следующий вывод на консоль:
Введите NewMas(2) = 2
В данном примере мы повторили содержание операции индексирования, но, естественно, алгоритм функции для перегрузки может быть любой.
31 Контейнеры: списки и массивы
Практически в любой программе, программной системе выполняется обработка множества объектов (переменных). Для совместного хранения этих объектов используется специальный тип объектов – контейнеры. Контейнеры предназначены для динамического включения, доступа, распечатки, сортировки и удаления объектов. Простейшим видом контейнеров является массив, в котором основной вид доступа к объектам выполняется по номеру (индексу). Данная ЛР посвящена изучению возможностей контейнеров и методов работы с объектами типа массив. Первоначально познакомимся с общими свойствами и разновидностями контейнеров.
32 Контейнеры
Контейнером называется специальная разновидность объектов, которые обеспечивают хранение объектов и доступ к этим объектам, а также их совместную обработку (например, печать, изменение и т.д.). Контейнеры-объекты создаются на основе контейнерных классов, которые имеют специальную структуру, специальные свойства и специальные операции. Примерами контейнеров в практике программирования являются, например:
-
окно интерфейса, в которое включены управляющие элементы интерфейса (поля ввода, кнопки и т.д.);
-
таблица базы данных (БД), в которой хранятся описания полей БД;
-
таблица записей БД, в которую включены отдельные записи.
-
объекты улицы с домами, расположенными на них
-
И многие другие.
Расширенный перечень примеров контейнерных классов вы можете найти в списке вариантов домашнего задания (ДЗ/КЛР) по дисциплине. Этот список размещен на сайте.
33 Контейнерные и элементные классы ДЗ
При создании контейнерного класса предполагается, что в программе будут использоваться объекты, которые будут включаться в контейнерные объекты. Такие объекты называются элементными, а классы, которые создаются для их описания – элементными классами. В зависимости от разновидностей контейнеров, элементных классов может быть много или один. Никаких ограничений на структуру элементных классов не накладывается, в частности одни контейнерные классы могут использоваться для накопления и контейнерных объектов. Для хранения разнородных объектов контейнеров они должны иметь специальную организацию.
В каждом варианте домашнего задания по дисциплине, студенты создают собственные контейнерные классы и собственные элементные классы. Для создания контейнерных классов можно использовать механизмы: наследования от типовых классов системы программирования, полная разработка контейнерных классов и примы шаблонов классов.
34 Разновидности контейнеров
Контейнеры могут иметь разные свойства, особенности и разную реализацию. Контейнерные объекты подразделяются: по размерности, по доступу, по упорядоченности, по универсальности и по типу создания.
По размерности контейнеры подразделяются на следующие типы:
-
С заданной размерностью (фиксированной) – например, стандартные массивы;
-
С динамической размерностью – например, списки;
-
С изменяемой размерностью – например, массивы с наращиваемой размерностью.
По доступу контейнеры подразделяются на следующие типы:
-
С прямым доступом по индексу (номеру) – например, массивы;
-
С прямым доступом по параметру (строка) – например, ассоциативные массивы;
-
С последовательным доступом – например, списки;
-
Со случайным доступом – например, множества.
По упорядоченности контейнеры подразделяются на следующие типы:
-
Упорядоченные контейнеры – например, массивы и списки;
-
Неупорядоченные контейнеры – например, множества и мультимножества;
По универсальности контейнеры подразделяются на следующие типы:
-
Контейнеры, содержащие однородные объекты (одного типа) – например, шаблонные контейнеры;
-
Контейнеры, содержащие разнородные объекты (разных типов) – например, объектные контейнеры;
По типу создания контейнеры подразделяются на следующие типы:
-
Шаблонные контейнеры, для создания которых типизируется элементные классы;
-
Объектные контейнеры, для создания которых используется базовый объектный класс, от которого наследуются рабочие элементные классы.
В некоторых контейнерных классах одновременно реализуются разные характеристики: есть контейнеры, обеспечивающие одновременно прямой доступ и последовательный доступ; контейнеры с фиксированной и наращиваемой размерностью.
Правильный выбор типа и свойств контейнеров необходим для грамотного решения поставленной задачи построения программы с использованием контейнеров.
35 Контейнеры - массивы
Простейшим видом контейнерного класса является массив. В базовый язык С/C++ включен стандартный вид массивов. Для этого в скобках описания указывается его размерность (допустимое число для хранения в контейнере), например, примеры описания стандартных массивов, рассмотрены ниже: