Недетерминированная множественность композиции 


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



ЗНАЕТЕ ЛИ ВЫ?

Недетерминированная множественность композиции



 

До сих пор был рассмотрен лишь случай, когда множественная композиция имела конечную детерминированную кратность. В частности, в примере о многоугольнике (Polygon) количество дочерних объектов в явном виде передавалось родительскому объекту-агрегату в конструкторе. Это число использовалось для выделения памяти массива дочерних объектов. Разумеется, массив всегда имеет конечный фиксированный размер. Изменить размер массива дочерних объектов в течение жизни объекта-агрегата было нельзя.

 

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

 

Предположим, местоположение вертолета в пространстве в течение вылета протоколируется с некоторой периодичностью, и затем путь подвергается той или иной форме обработки. Пусть для целей протоколирования используется следующий класс:

 

flightjournal.hpp

 

#ifndef _FLIGHTJOURNAL_HPP_

#define _FLIGHTJOURNAL_HPP_

 

//************************************************************************

 

#include "point3d.hpp"

 

//************************************************************************

 

class Helicopter;

 

//************************************************************************

 

// Класс, представляющий журнал полета вертолета

class FlightJournal

{

 

/*-----------------------------------------------------------------*/

 

public:

 

/*-----------------------------------------------------------------*/

 

// Конструктор с аргументами:

// - ссылка на связанный объект вертолет

// - максимальное количество фиксируемых точек

FlightJournal (

const Helicopter & _helicopter

, int _nMaxLocations

);

 

// Удаленный конструктор копий и оператор присвоения

FlightJournal (const FlightJournal &) = delete;

FlightJournal & operator = (const FlightJournal &) = delete;

 

// Деструктор

~FlightJournal ();

 

// Метод доступа к связанному с журналом вертолету

const Helicopter & getHelicopter () const;

 

// Метод для извлечения максимального количества точек для фиксации

int getMaxPositionsCount () const;

 

// Метод для извлечения количества уже зафиксированных точек

int getPositionsCount () const;

 

// Метод для извлечения координат конкретной зафиксированной точки

Point3D getPosition (int _index) const;

 

// Метод фиксации текущего местоположения вертолета

void trackPosition ();

 

// Метод вычисления длины пути вертолета в полете

double totalDistance () const;

 

/*-----------------------------------------------------------------*/

 

private:

 

/*-----------------------------------------------------------------*/

 

// Ссылка на объект-вертолет

const Helicopter & m_helicopter;

 

// Количество и массив данных точек местоположения

const int m_nMaxPositions;

Point3D * m_pPositions;

 

// Количество уже заполненных точек местоположения

int m_nUsedPositions;

 

/*-----------------------------------------------------------------*/

 

};

 

//************************************************************************

 

// Метод доступа к связанному с журналом вертолету

inline const Helicopter &

FlightJournal::getHelicopter () const

{

return m_helicopter;

}

 

//************************************************************************

 

// Метод для извлечения максимального количества точек для фиксации

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();

}

 

//************************************************************************

 

#endif // _FLIGHTHJOURNAL_V2_HPP_

 

 

flightjournal_v2.cpp

 

#include "flightjournal_v2.hpp"

#include "helicopter.hpp"

 

//************************************************************************

 

// Конструктор с аргументами

FlightJournal::FlightJournal (const Helicopter & _helicopter)

: m_helicopter(_helicopter)

// Конструктор вектора вызовется автоматически

{

// Никаких инвариантов проверять не нужно

}

 

//************************************************************************

 

// Метод для извлечения координат конкретной зафиксированной точки

Point3D FlightJournal::getPosition (int _index) const

{

// Проверку индекса осуществляет сам вектор

return m_positions.at(_index);

}

 

//************************************************************************

 

void FlightJournal::trackPosition ()

{

// Больше нет ограничений на количество точек.

// Просто извлекаем текущие координаты вектора и кладем их в вектор

Point3D currentPosition = m_helicopter.getCurrentPosition();

m_positions.push_back(currentPosition);

}

 

//************************************************************************

 

// Метод вычисления длины пути вертолета в полете

double FlightJournal::totalDistance () const

{

double result = 0.0;

for (int i = 0; i < m_positions.size() - 1; i++)

result += m_positions[ i ].distanceTo(m_positions[ i + 1 ]);

return result;

}

 

//************************************************************************

 

Новая структура выглядит в памяти следующим образом:

 

 

Разумеется, внутри объекта-вектора в той или иной форме хранится адрес блока данных с объектами Point3D в динамической памяти, количество используемых и выделенных элементов (чаще вместо хранения количества как числа, реализация стандартной библиотеки хранит адреса элементов, следующих в блоке данных за последним заполненным элементом и последним выделенным).

 

Помимо того, что такая реализация предоставляет решение основной проблемы - динамическое изменение количества фиксируемых позиций вертолета - благодаря использованию вектора удается достичь существенных упрощений реализации:

● нет необходимости в передаче и извлечении максимального числа точек;

● исчезает необходимость в явном деструкторе, поскольку деструктор вектора, который освободит выделенный блок памяти, будет автоматически вызван из генерируемого компилятором автоматического деструктора класса FlightJournal;

● исчезает необходимость в запрещении копирования, поскольку реализация класса std::vector обладает функциональностью корректного копирования своего содержимого, и никаких "висячих" указателей в автоматическом конструкторе копий не возникнет;

● вектору также можно доверить проверку индексов при доступе к конкретным точкам.

 



Поделиться:


Последнее изменение этой страницы: 2017-02-17; просмотров: 144; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

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