Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь КАТЕГОРИИ: АрхеологияБиология Генетика География Информатика История Логика Маркетинг Математика Менеджмент Механика Педагогика Религия Социология Технологии Физика Философия Финансы Химия Экология ТОП 10 на сайте Приготовление дезинфицирующих растворов различной концентрацииТехника нижней прямой подачи мяча. Франко-прусская война (причины и последствия) Организация работы процедурного кабинета Смысловое и механическое запоминание, их место и роль в усвоении знаний Коммуникативные барьеры и пути их преодоления Обработка изделий медицинского назначения многократного применения Образцы текста публицистического стиля Четыре типа изменения баланса Задачи с ответами для Всероссийской олимпиады по праву Мы поможем в написании ваших работ! ЗНАЕТЕ ЛИ ВЫ?
Влияние общества на человека
Приготовление дезинфицирующих растворов различной концентрации Практические работы по географии для 6 класса Организация работы процедурного кабинета Изменения в неживой природе осенью Уборка процедурного кабинета Сольфеджио. Все правила по сольфеджио Балочные системы. Определение реакций опор и моментов защемления |
ООП: Лекция 9. Композиция объектов.Стр 1 из 8Следующая ⇒
ООП: Лекция 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; просмотров: 149; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.217.182.45 (0.839 с.) |