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