![]() Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь FAQ Написать работу КАТЕГОРИИ: ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ТОП 10 на сайте Приготовление дезинфицирующих растворов различной концентрацииТехника нижней прямой подачи мяча. Франко-прусская война (причины и последствия) Организация работы процедурного кабинета Смысловое и механическое запоминание, их место и роль в усвоении знаний Коммуникативные барьеры и пути их преодоления Обработка изделий медицинского назначения многократного применения Образцы текста публицистического стиля Четыре типа изменения баланса Задачи с ответами для Всероссийской олимпиады по праву ![]() Мы поможем в написании ваших работ! ЗНАЕТЕ ЛИ ВЫ?
Влияние общества на человека
Приготовление дезинфицирующих растворов различной концентрации Практические работы по географии для 6 класса Организация работы процедурного кабинета Изменения в неживой природе осенью Уборка процедурного кабинета Сольфеджио. Все правила по сольфеджио Балочные системы. Определение реакций опор и моментов защемления |
ООП: Лекция 9. Композиция объектов.Содержание книги
Поиск на нашем сайте
ООП: Лекция 9. Композиция объектов. Версия 3.0 27 августа 2016г. (С) 2013-2016, Зайченко Сергей Александрович, к.т.н, ХНУРЭ, доцент кафедры АПВТ
Композиция объектов
КОМПОЗИЦИЯ (или агрегирование, включение) - простейший способ создания новых более сложных классов путем объединения нескольких объектов существующих классов в единое целое.
Между классом верхнего и нижнего уровня обычно присутствует отношение "целое-часть". Ниже приведен простейший пример композиции — объект двигатель (Engine) является частью объекта автомобиль (Car):
engine.hpp
#ifndef _ENGINE_HPP_ #define _ENGINE_HPP_
//************************************************************************
// Класс для двигателя class Engine {
/*-----------------------------------------------------------------*/
public:
/*-----------------------------------------------------------------*/
// Конструктор Engine (float _horsePower) : m_horsePower(_horsePower) {}
// Метод доступа к мощности float getHorsePower () const { return m_horsePower; }
/*-----------------------------------------------------------------*/
private:
/*-----------------------------------------------------------------*/
// Мощность в лошадиных силах const float m_horsePower;
/*-----------------------------------------------------------------*/
};
#endif // _ENGINE_HPP_
car.hpp
#ifndef _CAR_HPP_ #define _CAR_HPP_
//************************************************************************
#include <string> #include <iostream>
//************************************************************************
// Класс для автомобиля class Car {
/*-----------------------------------------------------------------*/
public:
/*-----------------------------------------------------------------*/
// Конструктор Car (const std::string & _model, float _engineHorsePower);
// Метод доступа к двигателю const Engine & getEngine () const;
// Метод доступа к названию модели const std::string & getModel () const
// Метод вывода описания машины в указанный поток void describe (std::ostream & _o) const
/*-----------------------------------------------------------------*/
private:
/*-----------------------------------------------------------------*/
// Название модели std::string m_model;
// Дочерний объект-двигатель Engine m_engine;
/*-----------------------------------------------------------------*/
};
//************************************************************************
// Метод доступа к дочернему объекту-двигателю inline const Engine & Car::getEngine () const { return m_engine; }
//************************************************************************
// Метод доступа к модели inline const std::string & Car::getModel () const { return m_model; }
//************************************************************************
#endif // _CAR_HPP_
car.cpp
//************************************************************************
#include “car.hpp”
//************************************************************************
// Конструктор Car::Car (const std::string & _model, float _engineHorsePower) : m_model(_model), m_engine(_engineHorsePower) // <- вызов конструктора дочернего объекта {}
//************************************************************************
// Тестовый метод, печатает информацию об автомобиле в поток void Car::describe (std::ostream & _o) const { _o << "Model: " << getModel() << ", horse power: " << m_engine.getHorsePower() << std::endl; // ^- обращение к дочернему объекту }
//************************************************************************
В памяти поля дочернего объекта размещаются внутри родительского объекта целиком:
К слову, объект std::string m_model также является дочерним объектом, и размещается полностью в родительском объекте Car, с той разницей, что модель - является объектом-значением, а двигатель - объектом-сущностью.
При обращении через указатель this родительского объекта добавляется смещение и получают указатель this на дочернем объекте. Никаких служебных данных времени выполнения для организации композиции в родительском объекте не требуется, все смещения дочерних объектов и других полей класса представляется возможным подсчитать автоматически во время компиляции.
В одном объекте может быть несколько дочерних объектов - одного или разных типов. Дочерние объекты могут выделяться как вместе с родительским, так и отдельно в динамической памяти. При этом родительский класс может быть ответственен либо не ответственен за уничтожение дочерних объектов в зависимости от ситуации. Дочерний объект может храниться по значению, если между ними имеется неразрывная зависимость от существования (по смыслу никогда нельзя отделить дочерний объект от родительского). Если зависимость менее жесткая и допускается замена или отцепление дочернего объекта, то обычно родительский объект хранит указатель на дочерний. Также может существовать слабая связь ссылочного типа, как правило, по константной ссылке, означающая логическое объединение понятий в некую группу, а не жесткую связь целое-часть.
Если дочерний объект хранится в родительском по значению, как двигатель в примере выше, и его конструктор имеет обязательные к указанию аргументы, то единственным местом их указания является список инициализации в конструкторе родительского класса:
// Конструктор Car::Car (const std::string & _model, float _engineHorsePower) : m_model(_model), // ^ вызов конструктора копий дочернего объекта №1 m_engine(_engineHorsePower ) // ^ вызов обычного конструктора дочернего объекта №2 { }
В свою очередь, дочерний объект-строка также инициализируется в списке инициализации, используя конструктор копий. Его присвоение возможно и в теле конструктора Car в отличие от двигателя, поскольку класс std::string имеет конструктор по умолчанию:
// Конструктор Car::Car (const std::string & _model, float _engineHorsePower) : m_engine(_engineHorsePower ) // ^ вызов обычного конструктора дочернего объекта №2 { m_model = _model; }
Однако такой подход может быть не эффективен с точки зрения производительности, если дочерний объект выделит избыточные ресурсы в своем конструкторе по умолчанию, которые затем вскоре будут освобождены и заменены присвоением в теле конструктора.
Вызов деструкторов дочерних объектов, хранящихся в родительском объекте по значению, размещается автоматически в деструкторе родительского класса. Это справедливо как для случая автоматически генерируемого деструктора родительского класса, так и для случая, когда имеется собственный деструктор. Явно деструктор дочерних объектов вызывать не требуется.
Если же дочерний объект хранится по указателю, и при этом родительский объект отвечает за его уничтожение, то обязательно нужно в явном виде удалять дочерний объект в деструкторе родительского класса. В противном случае произойдет утечка памяти. Обычно, для таких родительских классов полностью запрещают копирование и присвоение, во избежания некорректного разделения дочернего объекта.
Если дочерний объект хранится по значению, и при этом его конструктор копий или операторы присвоения/перемещения закрыты в зоне влияния спецификатора доступа private, на родительский объект автоматически распространяется такой же запрет.
Эти кратко описанные механизмы взаимодействия родительского и дочернего (дочерних) объектов подробно рассматриваются ниже в большом количестве примеров.
Inline double Circle::getRadius () const { return m_radius; }
//************************************************************************
#endif // _CIRCLE_HPP_
circle.cpp
#include "circle.hpp"
#define _USE_MATH_DEFINES // <- необходимо для работы с константой M_PI #include <cmath>
#include <stdexcept>
//************************************************************************
// Реализация конструктора Circle::Circle (Point3D _center, double _radius) : m_center(_center) // <= вызов конструктора копий Point3D , m_radius(_radius) { // Инвариант: радиус должен быть положительным if (m_radius <= 0.0) throw std::logic_error("Circle radius must be positive"); }
//************************************************************************
// Реализация метода вычисления длины окружности double Circle::getPerimeter () const { return 2.0 * M_PI * m_radius; }
//************************************************************************
// Реализация метода вычисления площади окружности double Circle::getArea () const { return M_PI * m_radius * m_radius; }
//************************************************************************
// Реализация метода вычисления координат точки на окружности под заданным углом Point3D Circle::getPointOn (double _angle) const { // Вычисляем длины катетов по гипотенузе (радиусу) и углу double xOffset = m_radius * cos(_angle); double yOffset = m_radius * sin(_angle);
// Создаем новый объект-точку return Point3D(m_center.getX() + xOffset, m_center.getY() + yOffset, m_center.getZ() ); }
//************************************************************************
В свою очередь, класс-цилиндр использует в качестве дочернего объекта окружности для моделирования основания. Аналогично предыдущему случаю, дочерний объект-окружность хранится в объекте-цилиндре по значению. А значит внутри объекта-цилиндра есть и объект-точка, представляющая собой центр окружности-основания:
cylinder.hpp
#ifndef _CYLINDER_HPP_ #define _CYLINDER_HPP_
//************************************************************************
#include " circle.hpp"
//************************************************************************
class Cylinder { /*-----------------------------------------------------------------*/
public:
/*-----------------------------------------------------------------*/
// Конструктор. Передаем основание по ссылке. // Передача основания по значению также допустима. Cylinder (Circle _base, double _height);
// Метод доступа к объекту-основанию Circle getBase () const;
// Метод доступа к высоте цилиндра double getHeight () const;
// Метод вычисления объема цилиндра double getVolume () const;
// Метод вычисления площади поверхности сторон double getSideSurfaceArea () const;
// Метод вычисления всей площади поверхности double getFullSurfaceArea () const;
// Метод вычисления координат точки на поверхности под определенным углом // и на заданной высоте Point3D getPointOnSurface (double _angle, double _height) const;
/*-----------------------------------------------------------------*/
private:
/*-----------------------------------------------------------------*/
// Основание цилиндра — дочерний объект класса Circle Circle m_base;
// Высота цилиндра double m_height;
/*-----------------------------------------------------------------*/
};
//************************************************************************
// Реализация метода доступа к объекту-основания inline Circle Cylinder::getBase () const { return m_base; }
//************************************************************************
// Реализация метода доступа к высоте Inline double Cylinder::getHeight () const { return m_height; }
//************************************************************************
#endif // _CYLINDER_HPP_
cylinder.cpp
//************************************************************************
#include "cylinder.hpp" #include <cmath> #include <stdexcept>
//************************************************************************
// Реализация конструктора Cylinder::Cylinder (const Circle & _base, double _height) : m_base(_base) // <= вызов конструктора копий класса Circle , m_height(_height) { // Инвариант: высота должна быть положительной if (m_height <= 0.0) throw std::logic_error("Cylinder height must be positive"); }
//************************************************************************
// Реализация метода вычисления объема double Cylinder::getVolume () const { return m_base.getArea() * m_height; }
//************************************************************************
// Реализация метода вычисления площади поверхности сторон double Cylinder::getSideSurfaceArea () const { return m_base.getPerimeter() * m_height; }
//************************************************************************
// Реализация метода вычисления площади полной поверхности double Cylinder::getFullSurfaceArea () const { return getSideSurfaceArea() + 2.0 * m_base.getArea(); }
//************************************************************************
// Реализация метода вычисления координат точки на поверхности Point3D Cylinder::getPointOnSurface (double _angle, double _height) const { // Вычисляем координаты точки на основании Point3D circlePoint = m_base.getPointOn(_angle);
// Создаем новую точку с учетом высоты return Point3D(circlePoint.getX(), }
//************************************************************************
Else throw std::logic_error("Point index is out of range"); }
//************************************************************************
// Реализация метода доступа к точке по индексу c правом на запись Point3D & Polygon::getPoint (int _index) { if (_index >= 0 && _index < getNPoints()) return m_points[ _index ]; Else throw std::logic_error("Point index is out of range"); }
//************************************************************************
// Реализация метода вычисления длины сторон многоугольника double Polygon::getPerimeter () const { // Суммируем расстояния между соседними точками многоугольника double total = 0.0; for (int i = 0; i < m_nPoints - 1; i++) total += m_points[ i ].distanceTo(m_points[ i + 1 ]); return total; }
//************************************************************************
Inline int Weapon::getCurrentAmmo () const { return m_currentAmmo; }
//************************************************************************
#endif //_WEAPON_HPP_
weapon.cpp
#include "weapon.hpp" #include "weapontype.hpp"
//************************************************************************
// Реализация конструктора Weapon::Weapon (const WeaponType & _type, int _initialAmmo) : m_type(_type) // запоминаем ссылку на дочерний объект , m_currentAmmo(_initialAmmo) { }
//************************************************************************
// Реализация метода определения полноты заряда bool Weapon::hasFullAmmo () const { // Сравниваем количество оставшихся зарядов с // максимально возможным для данного типа орудия return m_currentAmmo == getType().getMaxAmmo(); // ^ извлекаем максимум из дочернего объекта }
//************************************************************************
// Реализация метода определения отсутствия зарядов bool Weapon::hasNoAmmo () const { return m_currentAmmo == 0; }
//************************************************************************
// Реализация метода загрузки зарядов int Weapon::loadAmmo (int _ammo) { // Определяем количество не хватающих до полной загрузки зарядов int maxAmmo = getType().getMaxAmmo(); int freeAmmoSpace = maxAmmo - m_currentAmmo;
// Достаточно ли имеется зарядов до полной загрузки? if (_ammo >= freeAmmoSpace) { // Возвращаем число неиспользованных зарядов m_currentAmmo = maxAmmo; return _ammo - freeAmmoSpace; } Else { // Переданного количества зарядов не хватит для полной загрузки m_currentAmmo += _ammo; return 0; } }
//************************************************************************
// Реализация метода имитации выстрела bool Weapon::tryShoot () { // Имеется ли хоть один заряд? if (hasNoAmmo()) // Без зарядов стрелять трудно:-( return false;
// Выстрел произведен. Уменьшаем число оставшихся зарядов на 1. -- m_currentAmmo; return true; }
//************************************************************************
test_weapons.cpp
#include "weapon.hpp" #include "weapontype.hpp"
#include <cassert>
int main () { // Создаем тип орудия — калибр 125мм, максимум 5 зарядов WeaponType * pType = new WeaponType(125.0, 5);
// Создаем два орудия данного типа без начального заряда Weapon * pWeapon1 = new Weapon(* pType); Weapon * pWeapon2 = new Weapon(* pType);
// Заряжаем первое орудие максимально, убеждаемся, что орудие забрало // зарядов, соответствующее максимально возможному для типа орудия int totalAmmo = 100; totalAmmo = pWeapon1->loadAmmo(totalAmmo); assert(totalAmmo == (100 - pType->getMaxAmmo()));
// Заряжаем второе орудие только 1 снарядом pWeapon2->loadAmmo(1);
// Делаем выстрел из первого орудия, убеждаемся, что число зарядом уменьшилось на 1 assert(pWeapon1->getCurrentAmmo() == pType->getMaxAmmo()); bool success = pWeapon1->tryShoot(); assert(success && pWeapon1->getCurrentAmmo() == (pType->getMaxAmmo() - 1));
// Делаем выстрел из второго орудия assert(pWeapon2->getCurrentAmmo() == 1); success = pWeapon2->tryShoot(); // Убеждаемся, что выстрел произведен, и что зарядов в орудии не осталось assert(success && pWeapon2->hasNoAmmo());
// Второй выстрел из второго орудия не получится success = pWeapon2->tryShoot(); assert(! success);
// Уничтожаем орудия delete pWeapon1; delete pWeapon2;
// Уничтожаем тип орудия delete pType; }
Inline int Helicopter::getMachineID () const { return m_machineID; }
//************************************************************************
// Реализация метода доступа к текущей позиции inline Point3D Helicopter::getCurrentPosition () const { return m_position; }
//************************************************************************
// Реализация метода изменения текущей позиции Inline void Helicopter::moveTo (Point3D _p) { m_position = _p; }
//************************************************************************
// Реализация метода доступа к текущему углу inline Vector3D Helicopter::getCurrentAngle () const { return m_currentAngle; }
//************************************************************************
// Реализация метода изменения текущего угла Inline void Helicopter::turnTo (Vector3D _angle) { m_currentAngle = _angle; }
//************************************************************************
// Реализация метода подтверждения наличия орудия Inline bool Helicopter::hasWeapon () const { // Если орудие установлено, значение указателя будет отлично от nullptr return m_pWeapon!= nullptr; }
//************************************************************************
// Реализация метода доступа к установленному орудию Inline Weapon * Helicopter::getWeapon () const { // Даже если орудия не установлено, все корректно — метод вернет nullptr return m_pWeapon; }
//************************************************************************
#endif //_HELICOPTER_HPP_
helicopter.cpp
#include "helicopter.hpp" #include "weapon.hpp" // Включаем полное определение класса-орудия лишь сейчас, // поскольку для реализации методов класса-вертолета используется // содержимое класса-орудия (для выстрела, для уничтожения)
#include <stdexcept>
//************************************************************************
// Реализация конструктора Helicopter::Helicopter ( int _machineID , Vector3D _initialAngle , Point3D _initialPosition ) // Копируем номер, угол и координаты : m_machineID(_machineID) , m_currentAngle(_initialAngle) , m_position(_initialPosition) , m_pWeapon(nullptr) // <- Изначально орудие не устанавливается { }
//************************************************************************
// Реализация деструктора Helicopter::~Helicopter () { // Уничтожаем орудие, если оно установлено. // Если орудия не установлено, ничего страшного, delete nullptr игнорируется delete m_pWeapon; }
//************************************************************************
// Реализация метода установки орудия void Helicopter::installWeapon (Weapon & _weapon) { // Убеждаемся, что никакое другое орудие еще не было установлено if (hasWeapon()) throw std::logic_error("Weapon is already installed on the helicopter");
// Создаем связь между объектами: m_pWeapon = & _weapon; }
//************************************************************************
// Реализация метода снятия орудия void Helicopter::deinstallWeapon () { // Убеждаемся, что орудие было установлено if (! hasWeapon()) throw std::logic_error("Weapon was not installed on the helicopter");
// Разрываем связь между объектами через обнуление указателя m_pWeapon = nullptr;
// Примечание: при таком разрыве связи внешний код должен гарантировать // освобождение объекта-орудия, поскольку вертолет больше за него не отвечает }
//************************************************************************
// Реализация метода, осуществляющего пробу выстрела по заданной целевой координате bool Helicopter::tryShootingTargetAt(Point3D _p) { // Если орудия не установлено, выстрелить не получится if (! hasWeapon()) return false;
// Изменяем угол носа вертолета таким образом, чтобы вертолет смотрел на цель turnTo( Vector3D( _p.getX() - m_position.getX(), _p.getY() - m_position.getY(), _p.getZ() - m_position.getZ() ) );
// Пробуем выстрелить из орудия. // Логика учета количества зарядов уже учтена в реализации класса-орудия return m_pWeapon->tryShoot(); }
//************************************************************************
test_helicopter.cpp
#include "helicopter.hpp" #include "weapontype.hpp" #include "weapon.hpp"
#include <cassert>
int main () { // Создаем два объекта-вертолета Helicopter * pHelicopter1 = new Helicopter(1); Helicopter * pHelicopter2 = new Helicopter(2);
// Создаем тип орудия WeaponType * pWeaponType = new WeaponType(125.0, 5);
// Вооружаем первый вертолет орудием с одним начальным зарядом pHelicopter1->installWeapon(* new Weapon(* pWeaponType, 1)); assert(pHelicopter1->getWeapon()->getCurrentAmmo() == 1);
// Пробуем выстрелить c первого вертолета. // Первый выстрел успешен, а орудие разряжено. bool result = pHelicopter1->tryShootingTargetAt(Point3D(2.0, 2.0, 2.0)); assert(result && pHelicopter1->getWeapon()->hasNoAmmo());
// Пробуем выстрелить еще раз с первого вертолета. // Второй выстрел завершается неудачей, поскольку нет зарядов в орудии result = pHelicopter1->tryShootingTargetAt(Point3D(3.0, 3.0, 3.0)); assert(! result);
// Пробуем выстрелить со второго вертолета, однако терпим неудачу, // поскольку никакого орудия на нем не установлено result = pHelicopter2->tryShootingTargetAt(Point3D(4.0, 4.0, 4.0)); assert(! result);
// Куда же направлены носы наших вертолетов? // - первый вертолет имеет вооружение, и поворачивается перед попыткой выстрела, // соответственно, его нос направлен в сторону координаты последней цели // - второй вертолет не имеет установленного орудия, и не поворачивается assert(pHelicopter1->getCurrentAngle() == Vector3D(3.0, 3.0, 3.0)); assert(pHelicopter2->getCurrentAngle() == Vector3D(0.0, 0.0, 0.0));
// Уничтожаем оба вертолета. Орудие уничтожается вместе с первым вертолетом delete pHelicopter1; delete pHelicopter2;
// Уничтожаем тип орудия delete pWeaponType; }
Helicopterpad.hpp
#ifndef _HELICOPTERPAD_HPP_ #define _HELICOPTERPAD_HPP_
//************************************************************************
#include "point3d.hpp"
//************************************************************************
// Форвардное объявление класса-вертолета. // Его содержимое не требуется для объявления класса-площадки. class Helicopter;
//************************************************************************
// Класс-площадка class HelicopterPad {
/*-----------------------------------------------------------------*/
public:
/*-----------------------------------------------------------------*/
// Конструктор — задает координату местоположения, но не задает вертолета HelicopterPad (Point3D _location);
// Запрещенные конструктор копий и оператор присвоения HelicopterPad (const HelicopterPad &) = delete; HelicopterPad & operator = (const HelicopterPad &) = delete;
// Метод доступа к местоположению площадки Point3D getLocation () const;
// Метод подтверждения приземления вертолета bool hasLanded () const;
// Метод доступа к возможно приземлившемуся вертолету Helicopter * getLanded () const;
// Метод регистрации приземления вертолета void land (Helicopter & _helicopter);
// Метод регистрации взлета вертолета void unland ();
/*-----------------------------------------------------------------*/
private:
/*-----------------------------------------------------------------*/
// Координаты местоположения площадки. // В отличие от вертолета, координаты площадки не могут изменяться const Point3D m_location;
// Необязательная связь с приземлившимся объектом-вертолетом Helicopter * m_pLanded;
/*-----------------------------------------------------------------*/
};
//************************************************************************
// Реализация метода доступа к местоположению площадки inline Point3D HelicopterPad::getLocation () const { return m_location; }
//************************************************************************
// Реализация метода подтверждения приземления вертолета inline bool HelicopterPad::hasLanded () const { // Если вертолет приземлился, значение указателя будет отлично от nullptr return m_pLanded!= nullptr; }
//************************************************************************
// Реализация метода доступа к возможно приземлившемуся вертолету inline Helicopter * HelicopterPad::getLanded () const { // Даже если вертолет не приземлился, все корректно — метод вернет nullptr return m_pLanded; }
//************************************************************************
#endif //_HELICOPTERPAD_HPP_
helicopterpad.cpp
#include "helicopterpad.hpp" #include "helicopter.hpp" #include "point3d.hpp"
#include <stdexcept>
//************************************************************************
// Реализация конструктора HelicopterPad::HelicopterPad (Point3D _location) : m_location(_location), m_pLanded(nullptr) // Изначально вертолет не приземлялся { }
//************************************************************************
// Реализация метода регистрации приземления вертолета void HelicopterPad::land (Helicopter & _helicopter) { // Убеждаемся, что другой вертолет не приземлялся if (m_pLanded) throw std::logic_error("Something has already landed on this pad");
// Подводим вертолет к точке приземления _helicopter.moveTo(getLocation());
// Создаем связь между объектами — m_pLanded = & _helicopter; }
//************************************************************************
// Реализация метода регистрации взлета вертолета void HelicopterPad::unland () { // Убеждаемся, что вертолет приземлялся if (! m_pLanded) throw std::logic_error("No helicopter is currently landed");
// Вертолет должен взлететь на 7м строго над площадкой m_pLanded->moveTo( Point3D( getLocation().getX(), getLocation().getY(), getLocation().getZ() + 7.0 ) );
// Разрываем связь между объектами обнулением указателя m_pLanded = nullptr; }
//************************************************************************
test_pads.cpp
#include "helicopterpad.hpp" #include "helicopter.hpp"
#include <cassert>
int main () { // Создаем вертолетную площадку HelicopterPad * pHeliPad = new HelicopterPad(Point3D(1.0, 2.0, 0.0));
// Изначально на площадке никто не приземлен assert(! pHeliPad->hasLanded());
// Создаем вертолет Helicopter * pHelicopter = new Helicopter(1);
// Приземляем вертолет на площадке pHeliPad->land(* pHelicopter);
// Убеждаемся, что на площадке находится наш вертолет assert(pHeliPad->hasLanded() && pHeliPad->getLanded() == pHelicopter);
// Убеждаемся, что текущее местоположение вертолета совпадает с местом площадки Point3D location = pHelicopter->getCurrentPosition(); assert(location == pHeliPad->getLocation());
// Взлетаем pHeliPad->unland();
// На площадке не должно остаться вертолета assert(! pHeliPad->hasLanded());
// Координаты X/Y вертолета должны совпадать с координатами площадки, // однако координата Z должна отличаться (вертолет взлетает идеально вверх) Point3D newLocation = pHelicopter->getCurrentPosition(); assert(newLocation.getX() == location.getX() && newLocation.getY() == location.getY() && newLocation.getZ() > location.getZ());
// Уничтожаем площадку и вертолет delete pHeliPad; delete pHelicopter; } Inline int FlightJournal::getMaxPositionsCount () const { return m_nMaxPositions; }
//************************************************************************
// Метод для извлечения количества уже зафиксированных точек Inline int FlightJournal::getPositionsCount () const { return m_nUsedPositions; }
//************************************************************************
#endif // _FLIGHTJOURNAL_HPP_
flightjournal.cpp
#include "flightjournal.hpp" #include "helicopter.hpp"
#include <stdexcept>
//************************************************************************
// Конструктор с аргументами FlightJournal::FlightJournal ( const Helicopter & _helicopter, int _nMaxPositions ) : m_helicopter(_helicopter) , m_nMaxPositions(_nMaxPositions) , m_nUsedPositions(0) // <- Изначально нет данных о местоположении { // Инвариант: максимальное число позиций должно быть положительным if (m_nMaxPositions < 1) throw std::logic_error("Number of positions must be positive");
// Выделяем массив фиксированного размера для хранения координат позиций m_pPositions = new Point3D[ m_nMaxPositions ]; }
//************************************************************************
// Деструктор FlightJournal::~FlightJournal () { // Освобождаем массив координат delete[] m_pPositions; }
//************************************************************************
// Метод для извлечения координат конкретной зафиксированной точки Point3D FlightJournal::getPosition (int _index) const { // Проверяем корректность индекса точки if (_index >= 0 && _index < m_nUsedPositions) // Возвращаем координаты этой точки return m_pPositions[ _index ];
Else // Исключение: индекс позиции за допустимыми пределами throw std::logic_error("Position index is out of range"); }
//************************************************************************
// Метод фиксации текущего местоположения вертолета void FlightJournal::trackPosition () { // Проверяем наличие свободного места в массиве для очередной точки if (m_nUsedPositions == m_nMaxPositions) // Исключение: выделенная в конструкторе память исчерпана throw std::logic_error("No space left in the journal");
// Извлекаем текущие координаты вертолета и запоминаем его местоположение Point3D currentPosition = m_helicopter.getCurrentPosition(); m_pPositions[ m_nUsedPositions++ ] = currentPosition; }
//************************************************************************
// Метод вычисления длины пути вертолета в полете double FlightJournal::totalDistance () const { double result = 0.0; for (int i = 0; i < m_nUsedPositions - 1; i++) result += m_pPositions[ i ].distanceTo(m_pPositions[ i + 1 ]); return result; }
//************************************************************************
Структура объекта-журнала, инициализированного с максимальным количеством точек, равным 4, в памяти выглядит приблизительно следующим образом:
Основной проблемой данного решения является невозможность изменения количества фиксируемых местоположений вертолета. В этой задаче, как и в большинстве реальных задач, маловероятно, что конкретное число местоположений будет известно заранее. Это прекрасный случай применения ранее изученного STL-контейнера std::vector. Перепишем пример с журналом полета вертолета с применением вектора для хранения координат вертолета. Поскольку вектор полностью берет на себя задачу управления внутренним массивом данных с координатами в зафиксированных точках, реализация такого отношения существенно упрощается:
flightjournal_v2.hpp
#ifndef _FLIGHTHJOURNAL_V2_HPP_ #define _FLIGHTHJOURNAL_V2_HPP_
//************************************************************************
#include "point3d.hpp" #include <vector>
//************************************************************************
class Helicopter;
//************************************************************************
// Класс, представляющий журнал полета вертолета class FlightJournal {
/*-----------------------------------------------------------------*/
public:
/*-----------------------------------------------------------------*/
// Конструктор с только одним аргументом: ссылка на связанный объект-вертолет // Больше не нужно передавать максимальное количество позиций! FlightJournal (const Helicopter & _helicopter);
// Исчезает необходимость в запрещении конструктора копий и оператора присвоения // Деструктор тоже не нужен!
// Метод доступа к связанному с журналом вертолету const Helicopter & getHelicopter () const;
// Не нужен и метод, возвращающий максимальное количество позиций!
// Метод для извлечения количества уже зафиксированных точек int getPositionsCount () const;
// Метод для извлечения координат конкретной зафиксированной точки Point3D getPosition (int _index) const;
// Метод фиксации текущего местоположения вертолета void trackPosition ();
// Метод вычисления длины пути вертолета в полете double totalDistance () const;
/*-----------------------------------------------------------------*/
private:
/*-----------------------------------------------------------------*/
// Ссылка на объект-вертолет const Helicopter & m_helicopter;
// Вектор зафиксированных точек std::vector< Point3D > m_positions;
/*-----------------------------------------------------------------*/
};
//************************************************************************
// Метод доступа к связанному с журналом вертолету inline const Helicopter & FlightJournal::getHelicopter () const { return m_helicopter; }
//************************************************************************
// Метод для извлечения количества уже зафиксированных точек Inline int FlightJournal::getPositionsCount () const { return m_positions.size(); }
//************************************************************************
|
||||
Последнее изменение этой страницы: 2017-02-17; просмотров: 199; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 216.73.216.221 (0.013 с.) |