Множественная композиция объектов-сущностей 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Множественная композиция объектов-сущностей



 

Рассмотренный ранее пример содержал множественную композицию объектов-значений. Основная отличительная особенность любых объектов-значений - возможность свободного копирования и присвоение подобно встроенным типам. Наличие у объектов-точек такой возможности позволило легко поместить их в вектор.

 

Часто существует необходимость во множественной композиции объектов-сущностей, которые, как правило, не предполагают или даже полностью запрещают копирование, поскольку двух одинаковых объектов-сущностей в программе одновременно существовать не должно. Из невозможности копирования объектов вытекает невозможность помещения объектов в вектор. Также в вектор нельзя помещать ссылки на объекты, поскольку ссылка не является ни копируемой, ни перемещаемой в принципе. Очевидно, в таком случае остается лишь одно решение - вместо объектов или ссылок на объекты в вектор следует поместить указатели на объекты. Указатели могут копироваться и имеют значение по умолчанию не зависимо от типа данного (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; просмотров: 158; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.141.100.120 (0.074 с.)