§33 Классы. Описание класса. Экземпляры класса

Класс как абстрактный тип данных

При моделировании какого-либо процесса, явления, сущности нашего Мира мы создаем некоторую модель объекта. Эта модель является абстракцией реального объекта. При моделировании мы отвлекаемся от несущественных сторон, свойств, связей этого объекта, с целью выделения его наиболее существенных, закономерных признаков. Абстрагированием называют теоретическое обобщение такого отвлечения. Под абстракцией в объектно-ориентированного программирования понимают возможность работы с объектом, не вдаваясь в особенности его реализации, т. е. разделение интерфейса и реализации (см. ниже). Уже известным примером работы с объектами в программе является работа с обычными переменными.
В 10 классе мы познакомились с фундаментальными типами, такими как char, short, int, double. Каждый из этих типов определяет:

  • интервал допустимых значений данных этого типа;
  • операции применяемые с данными этого типа;
  • и способ организации памяти для хранения этих данных.

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

int a;

Данная инструкция говорит о том, что мы получили объект a (экземпляр) типа (класса) int. Поэтому программисты часто называют переменные — объектами.
Для создания моделей объектов в C++ используется классы. Классы представляют собой абстрактные типы данных (abstract data type), которые создаются разработчиком программы.

Описание класса

Для создания класса используется следующая синтаксическая конструкция:

class MyClass {
[private:]
// элементы в этой секции доступны только из класса; 
// это область доступа по умолчанию
public:
// элементы в этой секции доступны из любой части программы
};

где MyClass — это имя класса (абстрактный тип). В имени класса, по общему соглашению, первая буква должна быть заглавная. Спецификаторы доступа private и public управляют доступом к элементам класса. Для класса (в отличие от структуры) все его элементы недоступны, т. е. невидимы из-вне (по умолчанию), поэтому спецификатор private, можно не использовать.
Обратите внимание на то, что, в отличие от блока функции, блок описания класса заканчивается точкой с запятой. Большинство IDE вставляют ";" в конце блока автоматически.

Члены класса

К членам класса относятся:

  • данные-члены (или переменные-члены)
  • функции-члены

Функции-члены класса называются методами класса. А переменные-члены часто называются полями класса.
Не допускается инициализировать поля класса при описании. Инициализация производится в конструкторе класса (см. ниже), либо после создания объекта класса.
Секция private содержит члены класса, которые могут быть доступны только через функции-члены описанные в секции public.
Секция public содержит члены класса, которые образуют открытый интерфейс класса.
Методы класса могут быть определены как в самом классе, так и за его пределами. Если метод будет определяться вне класса, то в самом классе должен находиться прототип функции-члена.
В классе, по общепринятой практике, присутствуют два метода с префиксами set и get. set-метод используется для инициализации скрытых полей, а get-метод позволяет получить значения этих полей.
Создадим простой класс Car (машина). В этом классе моделируется движение автомобиля по трассе. Мы не закладываем в этот класс большой функционал (функциональность класса можно всегда расширить позже). Основные методы класса:

  • run определяет необходимый объем топлива для данного маршрута
  • refill определяет достаточность запасов горючего
  • timeWay определяет примерное время в пути
  • info выводит сведения о маршруте
  • card запрашивает параметры авто

Описание глобального класса производится вне функции main(), в том разделе, где мы ранее объявляли функции, после декларации using namespace.
Программа 30.1

//============================================================================
// Name        : lesson-30-1.cpp
//============================================================================
#include <iostream>
#include <cmath>
using namespace std;

class Car {
	private:
		int		id,
				vel, 	// скорость
				vol, 	// запас топлива
				way; 	// длина пути
		float	rate; 	// расход топлива
	public:
		void setAuto(int	myid,
					 int 	myVel,
					 int 	myVol,
					 int 	myWay,
					 float 	myRate) {
			id	 = myid;
			vel  = myVel;
			vol  = myVol;
			way  = myWay;
			rate = myRate;
		}
		int get_ID() { return id;  }
		int getVel() { return vel; }
		int getVol() { return vol; }
		int getWay() { return way; }
		float getRate() { return rate; }
		float run() { // Необходимый объем топлива
			return getWay() * getRate() / 100;
		}
		int refill() { // Остаток топлива
			return getVol() - run();
		}
		void timeWay() { // Время в пути
			cout << trunc(getWay() / getVel())
				 << " часов"
				 << endl;
		}
		void info() {
			cout << "Для " << get_ID() << "-го авто потребуется "
				 << run()
				 << " л топлива"
				 << endl;
			if (refill() <= 0)
				cout << "Вы должны пополнить запас топлива в пути";
			else
				cout << "Остаток горючего: "
					 << refill();
			cout << endl;
			cout << "Вы будете в пути: ";
			timeWay();
			cout << endl;
		}
		void card(int &par_id) {
			int par1, par2, par3;
			float par4;
			++par_id;
			cout << "Введите данные для " << par_id << "-го авто:\n";
			cout << "Средняя скорость, км/ч => ";
			while (cin >> par1)
				if (par1 > 200)
					cout << "Скорость не должна превышать 200 км/ч!\n"
						 << "Средняя скорость, км/ч => ";
				else
					break;
			cout << "Объем бензобака, л => ";
			cin >> par2;
			cout << "Расход топлива, л/100 км => ";
			cin >> par4;
			cout << "Длина пути, км => ";
			cin >> par3;
			setAuto(par_id, par1, par2, par3, par4);
			cout << endl;
		}
	};

int main() {
	int i(0); // id-авто
	Car auto1;
	Car *auto2 = new Car();
	// Заполняем карточку автомобиля
	auto1.card(i);
	auto2 -> card(i);
	// Получаем общие сведения
	auto1.info();
	auto2 -> info();
	// Первому авто понадобиться больше топлива?
	cout << boolalpha << (auto1.run() > auto2 -> run()) << endl;

	delete auto2;

	return 0;
}

Экземпляры класса

Объекты класса определяются точно также, как определяются переменные фундаментальных типов. Имя класса выступает как абстрактный тип. После указания типа следует идентификатор объекта класса. Объект иначе называется экземпляром класса. В нашей программе мы имеем два экземпляра класса Car: auto1 и auto2. Существуют два способа получения экземпляров класса. Оба этих способа используются в программе. Первый способ аналогичен объявлению обычной переменной. (Стр. 83). При этом автоматически выделяется (а после завершения работы — удаляется) память, достаточная для хранения всех полей класса. Второй способ основан на использовании операции new которая, как нам известно (см. здесь), запрашивает выделение динамической памяти. (Стр. 84). Следовательно, auto2 — это указатель на объект. Динамическая память должна быть освобождена (во избежании утечек памяти), когда работа с объектом будет закончена и он в программе станет ненужным. (Стр. 94).
Ниже показан пример работы программы:

Введите данные для 1-го авто:
Средняя скорость, км/ч => 250
Скорость не должна превышать 200 км/ч!
Средняя скорость, км/ч => 150
Объем бензобака, л => 80
Расход топлива, л/100 км => 7.7
Длина пути, км => 300

Введите данные для 2-го авто:
Средняя скорость, км/ч => 200
Объем бензобака, л => 90
Расход топлива, л/100 км => 10
Длина пути, км => 1200

Для 1-го авто потребуется 23.1 л топлива
Остаток горючего: 56
Вы будете в пути: 2 часов

Для 2-го авто потребуется 120 л топлива
Вы должны пополнить запас топлива в пути
Вы будете в пути: 6 часов

false

Доступ к методам класса

В нашей программе мы выполняем элементарные действия с экземплярами класса Car: производим инициализацию полей и вызываем методы. Область видимости не позволяет вызывать методы класса непосредственно, по имени, как обычную функцию. Для того, чтобы появилась возможность изменять объекты, посредством методов, используется операция «точка» или операция доступа к члену класса. Вызывая метод мы должны указать имя объекта, с которым связан данный метод, и имя самого вызываемого метода, разделенные точкой. (Стрр. 86, 89). Поскольку метод — это функция, то за именем метода следуют (). Общий синтаксис вызова метода для определенного объекта таков:
объект.метод(параметры)
Иначе обстоит дело, если используется указатель на объект. При использовании указателя обратиться к методу класса можно с помощью операций «точка» и разыменования:

(*auto2).card(i);

Но такое выражение выглядит неэстетично! (Оно соответствует стр. 87 программы 30.1). Поэтому, в C++, существует более элегантное решение — использование операции выбора элемента по указателю (->) или операция «стрелка». Тогда это выражение можно переписать следующим образом:

auto2 -> card(i);

В остальном, использование методов в программе ничем не отличается от использования функций. Если метод возвращает определенное значение соответствующего типа, то оно должно возвращаться в точку возврата, а это может быть переменная данного типа, операнд в сложном выражении или поток. (Стр. 92).
Также как и функции методы могут быть перегруженными.
Обратите внимание на скобки в стр. 92. Операции «точка» и «стрелка» имеют более высокий приоритет, по сравнению с операцией вставки, поэтому скобки — обязательны!

Область видимости. Операция ::

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

class Car {
	private:
		int		id,
				vel, 	
				vol, 	
				way; 	
		float	rate; 	
	public:
		void setAuto(int, int, int, int, float);
		int get_ID();
		int getVel();
		int getVol();
		int getWay();
		float getRate();
		float run();
		int refill();
		void timeWay();
		void info();
		void card(int&);
	};

Каждый класс в C++ использует свое пространство имен. Это сделано для того, чтобы избежать конфликтов при именовании переменных и функций. Для того, чтобы открыть доступ к именам вне тела класса необходимо использовать операцию доступа к области видимости (::). Синтаксис этой операции имеет следующий вид:
Тип имя_класса::имя_метода(параметры){…}
Синтаксические правила, существующие для описания функции, равно относятся и к описанию метода вне класса: количество параметров, их тип и порядок должны совпадать с таковыми в прототипе метода, находящегося в блоке класса. Так, например, будет выглядеть описание метода run:

float Car::run() { 
			return getWay() * getRate() / 100;
		}

Comments are closed