Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Лабы 1 курс 2 семестр / ЛР 8 Информатика 2 сем 2020

.pdf
Скачиваний:
11
Добавлен:
15.01.2021
Размер:
397.53 Кб
Скачать

9Лабораторная работа №8. Классы и объекты

9.1Цели и задачи работы:

Цель данной лабораторной работы — научиться работать с классами. Задача данной лабораторной работы — выполнить выданные типовые

задания (1 задачу).

9.2Теоретическая часть

Вязыке «C++» пользовательские типы данных могут содержать не только стандартные типы данных, как структуры, но и собственные функции. Созданный пользователем тип данных в терминах языка «C++» называется «классом», данные стандартных типов, входящие в него, - «свойствами класса», а входящие в него функции – «методами класса». Созданная переменная этого типа (экземпляр) класса называется «объектом».

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

struct MyVector

{

float x; float y;

// Метод GetLength() класса MyVector float GetLength()

{

//sqrt - функция для вычисления квадратного корня,

//описанная в заголовочном файле <math.h>

return sqrt(x * x + y * y);

}

};

Для вызова методов класса для его объектов используется оператор «.»:

MyVector vect; // Создаём вектор vect.x = 3;

vect.y = 4;

// Вычисляем его длину

float len = vect.GetLength();

// В переменную len будет записано значение 5

143

А для обращения обращаемся к методам и свойствам класса по указателю на его объект используется оператор «->»:

MyVector *pointer = new MyVector; // Создаём вектор pointer->x = 3;

pointer->y = 4;

// Вычисляем его длину

float len = pointer->GetLength();

// В переменную len будет записано значение 5

delete pointer;

При необходимости защитить свойства объекта класса от непосредственного изменения, т.е. что бы к ним нельзя было обращаться «напрямую», используя операторы «.» и «->», перед ними можно поставить спецификатор доступа «private:». Или сам класс можно объявить с использованием ключевого слова «class». А перед свойствами и методами, к которым допускается обращение через «.» и «->» тогда нужно поставить спецификатор доступа «public:».

class MyClass

{

int a;// К свойству a напрямую обращаться нельзя,

public:

//для задания его значения нужно вызвать метод: void setvalue(int val)

{

a = val;

}

//для получения его значения нужно вызвать метод: int getvalue()

{

return a;

}

}

144

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

Когда компилятор встречает в программе вызов метода класса через «.» или «->», перед которыми записан объект класса, или, соответственно, указатель на него, то он передаѐт в него первым аргументом (параметром) константный указатель на этот объект. Делает компилятор это автоматичски и неявно (скрыто). Такой указатель, хранящий адрес объекта класса, называется «this». При реализации метода класса указатель this можно не прописывать, компилятор его подставит сам. Например, такие записи эквивалентны:

void setvalue(int val)

void setvalue(int val)

{

{

this->a = val;

a = val;

}

}

А следующие записи – нет:

 

void setvalue(int a)

void setvalue(int a)

{

{

this->a = val;

a = a;

}

}

В первом случае всѐ будет работать, как и ожидалось. А во втором – ничего не произойдѐт из-за перекрытия имѐн переменных. Компилятор будет под «a» понимать исключительно параметр метода setvalue, как внутренней области видимости. Такие ситуации рекомендуется избегать, давая разные названия свойствам классов и параметрам их методов.

Разберѐм ещѐ такой случай:

struct Dog

{

void voice()

{

std::cout<<"Gav!";

}

};

145

void main()

{

Dog *myDog = nullptr; myDog->voice();

}

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

Теперь рассмотрим специальные методы для классов: «конструктор» и «деструктор», вызываемые автоматически при создании и при удалении объекта класса соответственно.

Конструктор – функция, предназначенная для инициализации объектов класса автоматически при создании. Для примера рассмотрим класс date:

class date

{

// объявление свойств класса unsigned int day, month, year;

public:

//объявление метода для инициализации свойств класса void set(unsigned int, unsigned int, unsigned int);

//(реализация здесь не приведена)

//объявление метода для получения значений свойств void get(unsigned int&, unsigned int&, unsigned int&);

//(реализация здесь не приведена)

};

...

// внутри main():

date d; // Создание объекта класса

unsigned int day, month, year;

d.get(day, month, year); // Получение значений его свойств

146

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

class date

{

...

public:

...

// объявление конструктора

void date(unsigned int, unsigned int, unsigned int); // (реализация здесь не приведена)

};

...

// внутри main():

date d(1, 2, 3); // Создание и инициализация объекта класса // Теперь создать объект класса, не инициализировав его свойства невозможно

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

Конструктор обеспечивает объектам класса надлежащую инициализацию, но для многих типов данных также требуется обратное действие (например, если класс использует динамическую память). Деструктор обеспечивает соответствующую очистку объектов указанного типа. Имя деструктора представляет собой имя класса с предшествующим ему знаком «~» (тильда).

// Тип данных «целочисленный массив» class Massiv

{

unsigned int quantity; // Количество данных int *data; // Указатель на данные

Massiv(unsigned int size)

{

quantity = size; // Запоминаем размер массива data = new int[quantity]; // Выделяем память

};

147

~Massiv()

{

delete[] data; // Освобождаем память при удалении объекта

}

};

9.3Примеры решения задач

Для примера возьмѐм задачу из предыдущего раздела.

Создать тип данных для хранения целочисленного массива произвольной длины. Написать функции для заполнения массива случайными значениями и для вывода на экран всех значений массива. Перегрузить оператор «+» для поэлементного сложения двух целочисленных массивов.

Блок-схемы алгоритмов работы методов класса (здесь не приведены) такие же, как и у функций работы со структурами в примере из предыдущего раздела. Разница только в реализации программным кодом.

//Для использования объектов cin и cout для ввода-вывода

#include <iostream>

//Для получения значения текущего времени для задания начального значения для генератора случайных чисел

#include <time.h>

//Тип данных «целочисленный массив»

class Massiv

{

unsigned int quantity; // Количество данных int *data; // Указатель на данные

public:

// Конструтор

Massiv(unsigned int size)

{

quantity = size; // Запоминаем размер

data = new int[quantity]; // Выделяем память

148

// Заполняем созданный массив нулями for(unsigned int i = 0; i < quantity; i++)

{

data[i] = 0;

}

}

// Конструтор копирования

Massiv(Massiv& m)

{

quantity = m.quantity; data = new int[quantity];

for(unsigned int i = 0; i < quantity; i++)

{

data[i] = m.data[i];

}

}

//Деструктор

~Massiv()

{

delete[] data;

}

//Заполнение массива случайными числами void Zapoln()

{

for(unsigned int i = 0; i < quantity; i++)

{

data[i] = rand() - RAND_MAX / 2;

}

}

//Для вывода элементов массива на экран

void Vivod()

{

for(unsigned int i = 0; i < quantity; i++)

{

std::cout<<data[i]<<"\t";

}

std::cout<<"\n\n";

}

149

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

Massiv& operator=(const Massiv& m)

{

delete[] data;

quantity = m.quantity; data = new int[quantity];

for(unsigned int i = 0; i < quantity; i++)

{

data[i] = m.data[i];

}

return *this;

}

//Перегрузка оператора "+" для сложения массивов

Massiv operator+(const Massiv& B)

{

//Создаём массив-результат, учитываем что складываемые массивы могут иметь разную длину

Massiv C(quantity > B.quantity ? quantity : B.quantity);

//Заносим в массив-результат суммы элементов массивоваргументов

const unsigned int temp = quantity < B.quantity ? quantity : B.quantity;

for(unsigned int i = 0; i < temp; i++)

{

C.data[i] = data[i] + B.data[i];

}

//Если один из массивов длинее другого, докопируем оставшиеся элементы

const int *temppointer = quantity > B.quantity ? data : B.data;

for(unsigned int i = temp; i < C.quantity; i++)

{

C.data[i] = temppointer[i];

}

return C;

}

};

150

// Для проверки работоспособности созданного класса напишем: void main()

{

srand(time(NULL));

// Создаём два массива

Massiv mass1(5); mass1.Zapoln(); mass1.Vivod();

Massiv mass2(3); mass2.Zapoln(); mass2.Vivod();

//Суммируем их в третий

Massiv mass3 = mass1 + mass2;

//Выводим результат на экран mass3.Vivod();

system("pause");

}

Тесты

Проведѐм несколько тестов созданного класса.

Размер: 5

-1886

 

2788

5564

971

-11380

 

Размер: 3

1388

 

-110

-14277

 

 

 

Сумма:

-498

 

2678

-8713

971

-11380

 

Размер: 4

-1716

 

4649

-15771 7079

 

 

Размер: 4

690

8970 -2102 -2655

 

 

Сумма:

-1026

 

13619

-17873 4424

 

 

Размер: 3

-1537

 

5988

-16282

 

 

 

Размер: 6

-12926

10935

-4436

5243

184

-13580

Сумма:

-14463

16923

-20718 5243 184

-13580

Массивы заполняются случайными значениями. Суммы массивов посчитаны правильно. Программа работает верно.

151

9.4Задания:

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

Допускается использовать внутри создаваемых функций другие пользовательские и библиотечные функции.

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

Задача 8.1.1. Создать класс для хранения строки символов. Реализовать конструктор для создания строки с заданным содержимым. Реализовать методы для замены содержимого строки и для получения указателя на содержимое строки. Перегрузить как методы операторы «+» и «+=» для соединия двух строк в одну.

Задача 8.1.2. Создать класс для хранения строки символов. Реализовать конструктор для создания строки с заданным содержимым. Реализовать методы для замены содержимого строки и для получения указателя на содержимое строки. Перегрузить как методы операторы «-» и «-=» для удаления из строки всех вхождений заданного символа.

Задача 8.1.3. Создать класс для хранения строки символов. Реализовать конструктор для создания строки с заданным содержимым. Реализовать методы для замены содержимого строки и для получения указателя на содержимое строки. Перегрузить как методы операторы «>>» и «<<» для циклического сдвига символов в строке на заданное число позиций.

Задача 8.1.4. Создать класс для хранения строки символов. Реализовать конструктор для создания строки с заданным содержимым. Реализовать методы для добавления символов к содержимому строки и для получения указателя на содержимое строки. Перегрузить как методы операторы «==» и «!=» для сравнения двух строк.

Задача 8.1.5. Создать класс для хранения строки символов. Реализовать конструктор для создания строки с заданным содержимым. Реализовать методы для добавления символов к содержимому строки и для получения указателя на содержимое строки. Перегрузить как метод оператор унарный «-» для изменения порядка символов в строке на обратный.

152