Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь FAQ Написать работу КАТЕГОРИИ: АрхеологияБиология Генетика География Информатика История Логика Маркетинг Математика Менеджмент Механика Педагогика Религия Социология Технологии Физика Философия Финансы Химия Экология ТОП 10 на сайте Приготовление дезинфицирующих растворов различной концентрацииТехника нижней прямой подачи мяча. Франко-прусская война (причины и последствия) Организация работы процедурного кабинета Смысловое и механическое запоминание, их место и роль в усвоении знаний Коммуникативные барьеры и пути их преодоления Обработка изделий медицинского назначения многократного применения Образцы текста публицистического стиля Четыре типа изменения баланса Задачи с ответами для Всероссийской олимпиады по праву Мы поможем в написании ваших работ! ЗНАЕТЕ ЛИ ВЫ?
Влияние общества на человека
Приготовление дезинфицирующих растворов различной концентрации Практические работы по географии для 6 класса Организация работы процедурного кабинета Изменения в неживой природе осенью Уборка процедурного кабинета Сольфеджио. Все правила по сольфеджио Балочные системы. Определение реакций опор и моментов защемления |
Множественная композиция объектов-сущностей↑ ⇐ ПредыдущаяСтр 8 из 8 Содержание книги
Поиск на нашем сайте
Рассмотренный ранее пример содержал множественную композицию объектов-значений. Основная отличительная особенность любых объектов-значений - возможность свободного копирования и присвоение подобно встроенным типам. Наличие у объектов-точек такой возможности позволило легко поместить их в вектор.
Часто существует необходимость во множественной композиции объектов-сущностей, которые, как правило, не предполагают или даже полностью запрещают копирование, поскольку двух одинаковых объектов-сущностей в программе одновременно существовать не должно. Из невозможности копирования объектов вытекает невозможность помещения объектов в вектор. Также в вектор нельзя помещать ссылки на объекты, поскольку ссылка не является ни копируемой, ни перемещаемой в принципе. Очевидно, в таком случае остается лишь одно решение - вместо объектов или ссылок на объекты в вектор следует поместить указатели на объекты. Указатели могут копироваться и имеют значение по умолчанию не зависимо от типа данного (nullptr).
Например, вертолетная площадка (HelicopterPad) могла бы вести учет объектов-вертолетов (Helicopter), которые на ней приземлялись в естественном хронологическом порядке:
helicopterpad.hpp
#ifndef _HELICOPTERPAD_HPP_ #define _HELICOPTERPAD_HPP_
//************************************************************************
#include "point3d.hpp" #include <vector>
//************************************************************************
// Форвардное объявление класса-вертолета. // Его содержимое не требуется для объявления класса-площадки. class Helicopter;
//************************************************************************
// Класс-площадка class HelicopterPad {
/*-----------------------------------------------------------------*/
public:
/*-----------------------------------------------------------------*/
...
// Метод регистрации приземления вертолета void land (Helicopter & _helicopter);
// Метод возвращает количество зарегистрированных приземлений int getRegisteredLandingsCount () const;
// Метод возвращает ссылку на объект-вертолет среди прежних приземлений Helicopter & getRegisteredLanded (int _index) const;
...
/*-----------------------------------------------------------------*/
private:
/*-----------------------------------------------------------------*/
// Последовательность вертолетов, приземлявшихся на данной площадке std::vector< Helicopter * > m_landingHistory;
/*-----------------------------------------------------------------*/
};
//************************************************************************
...
//************************************************************************
#endif //_HELICOPTERPAD_HPP_
helicopterpad.сpp
...
// Реализация метода регистрации приземления вертолета 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;
// Регистрируем приземление в журнал m_landingHistory.push_back(m_pLanded); }
//************************************************************************
// Метод возвращает количество зарегистрированных приземлений int HelicopterPad::getRegisteredLandingsCount () const { return m_landingHistory.size();
//************************************************************************
// Метод возвращает ссылку на объект-вертолет среди прежних приземлений Helicopter & HelicopterPad::getRegisteredLanded (int _index) const { return * m_landingHistory.at(_index);
//************************************************************************
Отдельного внимания требуют случаи, когда родительский объект является ответственным за уничтожение дочерних объектов-сущностей. Помещение указателя на некоторый объект в вектор не является причиной для его автоматического уничтожения (вектор не может догадаться о намерении программиста), и в таких случаях следует позаботиться об уничтожении вручную.
Ниже представлен класс, моделирующий эскадрилью вертолетов (HelicopterEscadrille). Конкретные вертолеты могут входить в состав эскадрильи (join), выходить из нее (leave), либо уничтожаться (onUnitDestroyed), например, в результате боевых действий. Для хранения связей между эскадрильей и индивидуальными вертолетами, используем контейнер std::vector с указателями на объекты Helicopter. Отличие этого примера от истории приземлений вертолетов на площадке состоит в ответственности за уничтожение объектов-вертолетов при уничтожении объекта-эскадрильи:
helicopterescadrille.hpp
#ifndef _HELICOPTER_ESCADRILLE_HPP_ #define _HELICOPTER_ESCADRILLE_HPP_
//************************************************************************
#include <vector>
//************************************************************************
// Форвардное объявление класса-вертолета class Helicopter;
//************************************************************************
// Класс-эскадрилья class HelicopterEscadrille {
/*-----------------------------------------------------------------*/
public:
/*-----------------------------------------------------------------*/
// Конструктор по умолчанию, необходим, т.к. имеется удаленный конструктор копий HelicopterEscadrille () = default;
// Удаленные конструктор копий и оператор присвоения HelicopterEscadrille (const HelicopterEscadrille &) = delete; HelicopterEscadrille & operator = (const HelicopterEscadrille &)= delete;
// Деструктор ~HelicopterEscadrille ();
// Метод, вычисляющий фактическое количество вертолетов int getJoinedUnitsCount () const;
// Метод доступа к вертолету по порядковому номеру Helicopter & getHelicopter (int _index) const;
// Метод поиска порядкового номера конкретного вертолета int findHelicopter (const Helicopter & _helicopter) const;
// Метод присоединения вертолета к эскадрилье void join (Helicopter * _pHelicopter);
// Метод выхода вертолета из состава эскадрильи void leave (Helicopter & _helicopter);
// Метод регистрации факта уничтожения вертолета void onUnitDestroyed (Helicopter * _pHelicopter);
/*-----------------------------------------------------------------*/
private:
/*-----------------------------------------------------------------*/
// Вектор указателей на объекты-вертолеты std::vector< Helicopter * > m_helicopters;
/*-----------------------------------------------------------------*/
};
//************************************************************************
// Метод, вычисляющий фактическое количество вертолетов Inline int HelicopterEscadrille::getJoinedUnitsCount () const { return m_helicopters.size(); }
//************************************************************************
#endif //_HELICOPTER_ESCADRILLE_HPP_
Helicopterescadrille.cpp
#include "helicopterescadrille.hpp" #include "helicopter.hpp"
//************************************************************************
// Реализация деструктора HelicopterEscadrille::~HelicopterEscadrille () { // Уничтожаем каждый из вертолетов for (Helicopter * pHelicopter: m_helicopters) delete pHelicopter; }
//************************************************************************
// Реализация метода доступа к вертолету по порядковому номеру Helicopter & HelicopterEscadrille::getHelicopter (int _index) const { return * m_helicopters.at(_index); }
//************************************************************************
// Реализация метода поиска порядкового номера конкретного вертолета Int HelicopterEscadrille::findHelicopter (const Helicopter & _helicopter) const { // Проходим по массиву указателей и ищем вертолет с таким же адресом int nHelicopters = m_helicopters.size(); for (int i = 0; i < nHelicopters; i++) if (m_helicopters[ i ] == (& _helicopter)) return i; // <- позиция обнаружена
return -1; // вертолет не обнаружен }
//************************************************************************
// Реализация метода присоединения вертолета к эскадрилье void HelicopterEscadrille::join (Helicopter * _pHelicopter) { // Убеждаемся, что данный вертолет уже не входит в состав эскадрильи if (findHelicopter(* _pHelicopter)!= -1) throw std::logic_error("Helicopter has already joined the escadrille");
// Помещаем адрес вертолета в конец вектора m_helicopters.push_back(_pHelicopter); }
//************************************************************************
// Метод выхода вертолета из состава эскадрильи void HelicopterEscadrille::leave (Helicopter & _helicopter) { // Убеждаемся в том, что вертолет входит в состав эскадрильи int position = findHelicopter(_helicopter); if ( position == -1) throw std::logic_error("Helicopter is not a part of this escadrille");
// Удаляем найденную позицию из вектора m_helicopters.erase(m_helicopters.begin() + position); }
//************************************************************************
// Метод регистрации факта уничтожения вертолета void HelicopterEscadrille::onUnitDestroyed (Helicopter * _pHelicopter) { // Выводим вертолет из состава эскадрильи leave(* _pHelicopter);
// Уничтожаем вертолет delete _pHelicopter; }
//************************************************************************
Для реализации эскадрильи нужен явный деструктор, уничтожающий сами дочерние объекты до момента автоматического освобождения внутренних данных вектора:
HelicopterEscadrille::~HelicopterEscadrille () { // Уничтожаем каждый из вертолетов for (Helicopter * pHelicopter: m_helicopters) delete pHelicopter;
// НЕЯВНО: // std::vector< Helicopter * >::~vector (& m_helicopters) }
Если этого действия не сделать, произодет утечка динамической памяти.
Ниже приведена тестовая программа, использующая вертолеты и эскадрильи:
test_escadrille.cpp
#include "helicopterescadrille.hpp" #include "helicopter.hpp"
#include <cassert>
int main () { // Создаем эскадрилью HelicopterEscadrille * pEscadrille = new HelicopterEscadrille();
// Создаем 3 вертолета Helicopter * pHelicopter1 = new Helicopter(1); Helicopter * pHelicopter2 = new Helicopter(2); Helicopter * pHelicopter3 = new Helicopter(3);
// Вводим все 3 вертолета в состав эскадрильи pEscadrille->join(pHelicopter1); pEscadrille->join(pHelicopter2); pEscadrille->join(pHelicopter3);
// Убеждаемся, что эскадрилья содержит 3 вертолета assert(pEscadrille->getJoinedUnitsCount() == 3);
// Выводим второй вертолет из состава эскадрильи. // При этом, эскадрилья снимает с себя ответственность за его уничтожение, // и это будет необходимо осуществить за пределами эскадрильи pEscadrille->leave(* pHelicopter2);
// В эскадрилье должно остаться 2 вертолета assert(pEscadrille->getJoinedUnitsCount() == 2);
// В результате боевых действий первый вертолет уничтожен. // Регистрируем данный факт в эскадрилье, что уничтожит объект-вертолет. pEscadrille->onUnitDestroyed(pHelicopter1);
// В эскадрилье должен был остаться только один вертолет. assert(pEscadrille->getJoinedUnitsCount() == 1);
// Восполняем потери. Создаем еще один вертолет и присоединяем его к эскадрилье Helicopter * pHelicopter4 = new Helicopter(4); pEscadrille->join(pHelicopter4);
// Теперь в ней 2 вертолета assert(pEscadrille->getJoinedUnitsCount() == 2);
// Уничтожаем эскадрилью и отдельно вертолет, не входящий в ее состав delete pEscadrille; delete pHelicopter2; }
//************************************************************************
Полные примеры из лекции
https://github.com/zaychenko-sergei/oop-samples/tree/master/lec9
Выводы
В ходе данной лекции на наглядных примерах был показан принцип объектной композиции, один из ключевых способов составления сложных иерархий объектов, организующихся по принципу целое-часть. Были показаны разновидности отношения композиции и особенности их реализации средствами языка С++. Отношения отличались кратностью (одна связь или много связей), стационарностью (возможность разрыва связи) и наличием/отсутствием ответственности за уничтожение дочерних объектов.
Множественная кратность редко бывает фиксированной в практических задачах, что является удачным контекстом для применения STL-контейнеров, таких как std::vector. Контейнеры могут хранить примитивные значения и копируемые объекты-значения, а также указатели на объекты-сущности. Контейнеры отвечают за автоматическое освобождение собственной памяти, но не занимаются уничтожением дочерних объектов. Реализация ответственности за уничтожение связанных объектов, в случае необходимости, является задачей программиста, а не контейнера.
|
||||
Последнее изменение этой страницы: 2017-02-17; просмотров: 183; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.191.200.47 (0.008 с.) |