Всё остальное – стр. № 33. Лабораторная работа 1. Классы и объекты в С++
Цель работы
Ознакомление с технологией построения классов, разработкой методов, дружественных функций и перегрузкой операций.
Основные сведения
Базовыми понятиями объектно-ориентированной программы являются «объект класса» и «класс». Эти понятия аналогичны понятиям переменной и её типа. Класс еще называют абстрактным типом данных (АТД). Компонентами класса являются переменные базовых типов языка и других классов, объявленных в программе, и функции, работающие с этими переменными. Обычно переменные, определенные в классе, называют полями или данными, а функции – методами класса. В С++ имеется возможность управлять доступом к полям и методам класса, в частности, введение такого порядка, при котором доступ к полям класса имеют только его методы. Объединение данных и методов работы с ними в одну языковую конструкцию с возможностью управления доступом к данным называется инкапсуляцией.
Тип класса может быть задан одним из 3-х атрибутов: class, struct, union. Имя класса становится идентификатоpом нового типа данных. Полное имя компоненты класса записывается следующим образом:
[тип_компоненты] имя_класса:: имя_компоненты, где символ :: называется оператором принадлежности. Если перед этим оператором нет имени класса, то имя определяется как глобальное, известное за пределами классов.
Для создания объекта используется так называемый конструктор класса. Конструктор похож на обычную функцию с именем, совпадающим с именем класса; конструктор не имеет типа возвращаемого значения.
Объект класса может быть создан в памяти, управляемой компилятором, и в динамической памяти с помощью функции new. В первом случае конструктор инициализирует поля объекта, во втором еще и создаёт объект. В классе может быть записано несколько конструкторов; отличаться друг от друга они должны списком параметров. Конструктор класса X может иметь любой параметр любого типа, кроме X; допускается параметр типа X& (ссылку на объект типа X). Конструктор класса X заданный в виде X(X&) называется конструктором копирования; он подразумевает создание нового объекта в свободной памяти и копирование в него ранее созданного объекта.
Если в классе не задан конструктор, то компилятор автоматически использует для создания объекта конструктор по умолчанию, который имеет пустой список параметров и пустое тело. Если в классе записан какой-либо конструктор, отличающийся от конструктора по умолчанию, а в программе требуется использовать как раз последний (например, для создания массива объектов), то программист должен определить его в классе.
Деструктор автоматически разрушает объект при выходе последнего из области видимости; деструктору предшествует символ ~. Деструктор не имеет аргументов и не возвращает значения. В классе может быть только один деструктор. Если деструктор не задан в программе, то он генерируется компилятором. Если при создании объекта в конструкторе используется оператор new, то в деструкторе должен быть использован оператор delete.
Объявление объекта класса должно соответствовать имеющемуся конструктору. Способы создания объектов те же, что и в языке С: объект может объявляться как простая переменная, массив, указатель. Для создания массива объектов необходимо иметь в классе конструктор без параметров или конструктор, у которого все параметры заданы по умолчанию.
Локальные нестатические поля класса не могут инициализироваться при их объявлении в классе. Инициализация таких полей возможна только при создании объекта с помощью соответствующего конструктора.
Доступ к компоненту определяется наличием (или отсутствием) в определении класса атрибутов доступа public, private, protected. Атрибут public определяет все следующие за ним компоненты как открытые, к которым разрешен прямой доступ из любой точки программы; privateзапрещает прямой доступ к соответствующим компонентам извне (закрытые компоненты). В классе типа class все компоненты по умолчанию private, в классах типа struct – public. Каждый атрибут, записанный в классе, отменяет действие предыдущего. (Атрибут protected –защищенный – используется в иерархии классов).
Принятый способ использования атрибутов: данные объявляются типа private (protected в базовых классах), методы и конструктор – типа public.
Обращение к объектам-компонентам класса из текста программы вне класса может быть двояким: имя_объекта.имя_компоненты или имя_объекта->имя_компоненты. Во втором случае объект должен быть создан в свободной памяти посредством указателя. При этом предполагается, что поля класса имеют атрибут public. Если поле класса закрыто, то обращение к нему производится через открытый метод класса.
Кроме методов доступ к закрытым компонентам класса имеют так называемые дружественные функции. Они не являются компонентами класса и объявляются в следующем виде:
friend тип имя (параметры);
Дружественные функции рекомендуется использовать в следующих случаях:
– при необходимости упростить форму обращения к компонентам класса: не будучи компонентом класса, дружественная функция при вызове не требует имени объекта;
– при необходимости использовать методы одного класса для обработки закрытых данных другого класса.
Перегрузка операций
К перегрузке стандартных операций прибегают в тех случаях, когда хотят расширить действие операции на типы операндов, отличающихся от определенных в стандарте языка, или вообще изменить смысл знака операции. Напpимеp, стандаpтная опеpация сложения может быть пеpегpужена для стpоковых данных, комплексных чисел и т.п. Естественно, что пеpегpужен-ность сохpаняет смысл только для данных того класса, в котором она пpоизведена. Перегрузка операций производится с помощью специальной функции operator согласно следующей форме:
тип operatorзнак_опеpации (типы аpгументов){... }.
При этом оператор-функция может быть компонентом класса или дружественной функцией.
Перегружаться могут практически все, за небольшим исключением, операции. При этом функция operator не изменяет приоритет операции и число операндов, определенных в стандартной операции.
Примеры перегрузки операций приведены ниже в определении класса Complex.
Агрегация
Два класса могут быть связаны отношением агрегации (включения). Отношение агрегации (целое/часть) между классами имеет место, когда один класс (целое) содержит в качестве компоненты объект другого класса (часть). Например, один пользовательский класс содержит компоненту, тип которой определяется другим пользовательским классом. Различают строгую и нестрогую агрегацию. В первом случае часть включается в целое как значение, во втором – в виде указателя. (Строгая агрегация еще называется композицией). На диаграммах агрегация изображается сплошной линией от класса, объект которого является частью, до класса, в который включается этот объект; линия заканчивается пустым ромбом в случае нестрогой агрегации и зачерненным в противном случае).
Например,
class Point{
public:
double x, y;
/*…..*/
};
class Triangle {
public:
Point x1, x2, x3;
/*…..*/
};
Здесь следует отметить, пожалуй, вот что. При обращении к полю или методу включенного класса из объекта включающего, вид обращения может быть таким:
void main()
{ Triangle t1;
t1.x1.x=5;
t1.x2.y=7;
……}
С точки зрения инкапсуляции данных такое обращение, хотя и работающее, но неправильное – поля класса Triangle открыты. Да и обращение с двумя точками выглядит не очень красиво. Закроем поля в Triangle.
Отношение включения рекомендуется использовать в некоторых вариантах лаб. работ, там, где оно выглядит естественным. Например, игральная карта и колода карт. Между тем, заметьте, что записывая Triangle t1 в main мы тоже, по большому счету, занимаемся включением.Так, конечно, не говорят – main не класс, а функция. ( В C#, кстати, main является членом класса.) Предположим, что мы решили обойтись одним классом Карта. Очевидно, что колода является массивом карт и что мы должны тогда в main организовывать массив и его обработку. В ООП та конструкция, которая управляет программой, называется клиентом. В клиенте не должны быть видны подробности организации программы, в частности, поля классов и действия клиента должны быть максимально простыми. Используя дополнительный класс и включение мы можем решить эти задачи.
В первой работе, которая предназначена для изучения основ, я не настаиваю на упрощении main, но, начиная со 2-й, это требование должно соблюдаться.
Пример выполнения работы
Ниже приводится определeние класса комплексных чисел Complex, в котором ввод чисел производится в алгебраической форме, операции умножения и деления производятся в полярной системе координат.
Графическое изображение класса включает имя класса, его поля и методы. (В скобках даны русские названия, которые необязательно являются буквальным переводом.) На диаграмме закрытому компоненту предшествует символ -, открытому – символ +.
Ниже приведена программа реализация класса Complex в проекте на Visual Studio. Проект включает 3 файла: заголовочный - Complex.h , реализации - Complex.cpp и функции main - MyComplex.cpp.
//complex.h
#pragma once
class Complex
{
double real;
double image;
public:
Complex();
void InputComplex();
void ShowComplex () const;
//Перегрузка операций сложения, умножения и деления комплексных чисел
Complex operator + (Complex);
Complex operator * (Complex );
Complex operator / (Complex);
~Complex(void);
};
//сommplex.cpp
#include "Complex.h"
#include <iostream>
#include <math.h>
using namespace std;
Complex::Complex(void){ }
Complex::~Complex(void) { }
//Ввод дествительной и мнимой части
void Complex::InputComplex() {
double re,im;
cout<<"Input real part "; cin>>re;
cout<<"Input image part "; cin>>im;
real=re;
image=im;
}
//Вывод числа на консоль
void Complex::ShowComplex()const{
cout<<real<<" +i"<<image<<endl;
}
/*Перегруженные операции +, * и /.
При выполнении операций умножения и деления производится преобразование комплексного числа из введенной прямоугольной системы в полярные координаты, в которых и выполняется соответствующая операция. После операции производится обратное преобразование координат*/
Complex Complex:: operator + (Complex t)
{ Complex tmp;
tmp.real=real+t.real;
tmp.image=image+t.image;
return tmp;
}
Complex Complex::operator *(Complex t){
Complex tmp;
double arg, mod, arg1, mod1;
// Вычисление модулей аргументов операции
mod=sqrt(pow(real,2)+pow(image,2));
mod1=sqrt(pow(t.real, 2)+pow (t.image,2));
//Вычисление и проверка углов
if (real==0) { cout<< "denominator = 0";int g; cin<<g; exit(1);}
if(real>0)
{
if(image>0)
arg=(atan(image/real));
else arg=6.28+atan(image/real);
}
else arg=3.14+atan(image/real);
if(t.real>0)
{
if(t.image>0)
arg1=atan(t.image/t.real);
else arg1=6.28+atan(t.image/t.real);
}
else arg1=3.14+atan(t.image/t.real);
tmp.real=mod*mod1*(cos(arg+arg1));
tmp.image=mod*mod1*(sin(arg+arg1));
return tmp;
}
//Перегрузка операции деления
Complex Complex::operator / (Complex t)
{
Complex tmp;
double arg,mod,arg1,mod1;
mod=sqrt(pow(real,2)+pow(image,2));
mod1=sqrt(pow(t.real, 2)+pow (t.image,2));
if(real==0) throw 1;
if(real>0)
//Преобразование координат
if(image>0) arg=atan(image/real);
else arg=6.28+atan(image/real);
else arg=3.14+atan(image/real);
if(t.real>0)
{
if(t.image>0) arg1=atan(t.image/t.real);
else arg1=6.28+atan(t.image/t.real);
}
else arg1=3.14+atan(t.image/t.real);
if (mod1==0) throw 1;
tmp.real=(mod*mod1*cos(arg-arg1))/(mod1*mod1);
tmp.image=(mod*mod1*sin(arg-arg1))/(mod1*mod1);
return tmp;
}
//MyComplex.cpp
#include "stdafx.h"
#include "Complex.h"
int _tmain(int argc, _TCHAR* argv[])
{
Complex cm1, cm2, cm3;
cm1.InputComplex();
cm2.InputComplex();
cm3=cm1+cm2;
cm3.ShowComplex();
cm3=cm1*cm2;
cm3.ShowComplex();
return 0;
}
Варианты заданий
При выполнении работы необходимо:
- разработать соответствующие классы, их поля и методы;
- создать в классах не менее 2-х конструкторов: по умолчанию и с параметрами; если в каком-либо конструкторе используется функция new, то следует дополнительно создать конструктор копирования;
- поля класса сделать закрытыми, для чтения и изменения их значений определить открытые методы;
- в соответствие с вариантом определить методы работы с объектами;
- в функции main создать объекты для демонстрации работы методов класса c выводом соответствующих сообщений на монитор .
Усложненный вариант задания (необязательный) предполагает использование при создании объектов паттерн “Одиночка” [3]. Включите его в программу так, чтобы можно было создавать ровно столько объектов, сколько требуется для демонстрации работы программы;
В приложении к работе приводятся рекомендации по перекодировке латинского текста в кириллицу.
Защита этой и остальных работ предполагает обязательную демонстрацию студентами своих навыков работы в выбранной среде программирования: умения найти и исправить ошибки, указываемые компилятором, извлечь полезную информацию из его предупреждений, использования режима отладки и т.п.
1. Определить класс «Дата», в котором производится ввод календарной даты, определение соответствующего дате дня недели, прибавления к дате целой константы и т.п. операции.
2. Определить класс », в котором производятся операции над обыкновенными дробями (сравнение, сложение, умножение, деление, сокращение).
3. Определить класс римских цифр. Реализовать алгоритм перевода из десятичной системы и обратно и операции сложения и вычитания.
4. Определить класс “Точка на шаровой поверхности”, объекты которого задаются в полярной системе координат (широта, долгота, радиус-вектор). Реализовать операцию определения длины линии на поверхности шара между двумя точками.
5. Определить класс прямоугольников со сторонами, параллельными осям координат. Операции: перемещение, изменение размеров, построение прямоугольника, являющегося пересечением двух других.
6. Определить класс многочленов от одной переменной, задаваемых степенью многочлена и массивом коэффициентов, с операциями вычисления значения многочлена для заданного аргумента, сложения и вычитания многочленов.
7. Определить класс «Множество» c методами: добавление элементов, пересечение и объединение множеств. Множество реализовать на основе массива в динамической памяти или списка. Определить функцию ввода элементов с проверкой вводимых данных .
8. Определить класс вещественных матриц с методами, реализующими сложение и вычитание матриц, транспонирования, вычисления детерминанта. Воспользоваться массивами, создающимися с помощью оператора new.
9. Написать класс для работы с С-строкой ( завершающим символом строки является '\0'). Поля класcа: указатель на char – хранит адрес динамически выделенной памяти для размещения символов строки, значение типа int для хранения длины строки в байтах. Конструкторы: без параметров, копирования, создающий строку заданной длины. Методы: получение длины строки, очистка строки, сравнение и выделение подстроки, соединения 2 – х строк (перегрузить операцию +). Не использовать соответствующие библиотечные функции.
10. Реализовать на основе списка класс «Стек» с операциями инициализации, добавления и извлечения элементов согласно соответствующей дисциплине обслуживания.
11. Реализовать на основе списка класс «Очередь» с операциями инициализации, добавления и извлечения элементов согласно соответствующей дисциплине обслуживания.
12. Описать класс «Предметный указатель». Каждая компонента указателя содержит слово и номера страниц, на которых это слово встречается. Количество номеров страниц, относящихся к одному слову, изменяется от одного до десяти. Предусмотреть возможность формирования указателя с клавиатуры, вывода указателя, вывода номеров страниц для заданного слова, удаления элемента из указателя.
13. Определить класс « Аэропорт», полями которого являются взлетно-посадочная полоса (ВПП), самолет и пассажирский терминал. Реализовать методы, осуществляющую следующую последовательность действий: а) ВПП занята самолетом, собирающимся взлетать, в терминале находятся пассажиры этого рейса; б) пассажиры перемещаются в самолет, терминал освобождается; в) самолет взлетает, ВПП освобождается.
14. В следующих вариантах рекомендуется создавать 2 класса и объекты или указатели на один из них включать в другой (агрегация классов)
Определить классы «Автостоянка» и «Автомобиль». Для каждого автомобиля во втором классе описывают госномер, марку, цвет и признак присутствия на стоянке. В первый класс вводится массив автомобилей, которые могут присутствовать на стоянке. Определить в первом классе методы заезда автомобиля на стоянку, его выезда, подтверждения присутствия автомобиля на стоянке по госномеру, вывода списка присутствующих и уехавших автомобилей.
15. Определить классы «Карта» и «Колода карт». Поля первого – масть и достоинство. Второй содержит массив объектов первого и методы инициализации, раздачи карт на заданное число игроков равными порциями, перемешивание колоды.
16. Определить классы «Автобусный маршрут» и «Автобус». Во втором задаются госномер, вместимость автобуса и признаки полный /пустой, неподвижен/двигается. Методы этого класса: заполнение автобуса пассажирами, освобождение его, переход к движению, остановка. Объект этого класса вводится в первый класс; наряду с ним в этом классе определяется массив названий остановок конкретного маршрута. В классе необходимо реализовать следующую последовательность действий: заполнение автобуса, перемещение его от одной остановки к другой, освобождение.
17. Определить классы «Книга» и «Библиотека». Поля книги: ФИО автора(ов), название, год издания, издательcтво. Библиотека содержит массив книг, размер которого является параметром. Массив инициализируется 10-ю реальными книгами. Предусмотреть возможность поиска книги по какому-либо признаку (например, по автору или по году издания), сортировки книг по разным полям. Разработать метод добавления книг в библиотеку с увеличением размера массива.
18. Определить классы «Студент» и «Студенческая группа». Поля первого: ФИО, дата рождения, адрес, учится/отчислен. Группа содержит массив студентов, размер которого является параметром. Предусмотреть возможность поиска студента по какому-либо признаку, сортировки по разным полям, изменения значения поля учится/отчислен, выдачи списка учащихся и отчисленных студентов.
Контрольные вопросы
1. Для чего служит конструктор ? Может ли в классе быть несколько конструкторов? Чем должны отличаться различные конструкторы одного и того же класса?
2. Для чего служит деструктор класса? Имеет ли деструктор параметры? В каком случае в тело деструктора включается оператор delete?
3. Какие сообщения и в какой последовательности будут выведены на монитор?
Запишите обращения к компоненте abc с использованием точки и стрелки.
5. Каким образом компилятор отличает вызов стандартной операции от вызова перегруженной? Вспомните язык С и его операции <<, >> и запишите результат второго выражения
int a=4;
cout<<(a<<3);
6. Найдите ошибку в следующем фрагменте программы: