Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ООП_ Лекция №09 - Композиция объектов.docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
754.21 Кб
Скачать

Ссылочная композиция — логическое объединение

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

Если такая связь между объектами является постоянной, то, как правило, дочерний объект хранится по ссылке либо константной ссылке, которая передается в родительский объект при конструировании. Обычно, таким видам объектов не свойственно копирование и присвоение, и эти операции имеет смысл в явном виде запретить.

Ниже представлено логическое объединение между орудием (Weapon) и его типом, или моделью (WeaponType). Каждый экземпляр класса Weapon получает ссылку на тип орудия WeaponType в конструкторе, и эта ссылка сохраняется до конца жизни объекта орудия. Предполагается, что тип орудия просуществует дольше любого из орудий. Это ограничение должно обеспечиваться внешним по отношению к рассматриваемым классам кодом.

weapontype.hpp

#ifndef _WEAPONTYPE_HPP_

#define _WEAPONTYPE_HPP_

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

class WeaponType

{

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

public:

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

// Конструктор

WeaponType ( double _caliber, int _maxAmmo );

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

WeaponType ( const WeaponType & ) = delete;

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

// Метод доступа к калибру

double getCaliber () const;

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

int getMaxAmmo () const;

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

private:

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

// Калибр орудия

const double m_caliber;

// Максимальное число зарядов

const int m_maxAmmo;

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

};

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

// Реализация метода доступа к калибру

inline double WeaponType::getCaliber () const

{

return m_caliber;

}

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

// Реализация метода доступа к максимальному числу зарядов

inline int WeaponType::getMaxAmmo () const

{

return m_maxAmmo;

}

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

#endif // _WEAPONTYPE_HPP_

weapontype.cpp

#include "weapontype.hpp"

#include <stdexcept>

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

// Реализация конструктора

WeaponType::WeaponType ( double _caliber, int _maxAmmo )

: m_caliber( _caliber ), m_maxAmmo( _maxAmmo )

{

// Инвариант: калибр является положительным числом

if ( _caliber <= 0.0 )

throw std::logic_error( "Expecting a positive caliber" );

// Инвариант: максимальный заряд является положительным числом

if ( _maxAmmo < 1 )

throw std::logic_error( "Expecting at least 1 as a maximum ammo" );

}

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

weapon.hpp

#ifndef _WEAPON_HPP_

#define _WEAPON_HPP_

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

class WeaponType;

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

class Weapon

{

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

public:

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

// Конструктор

Weapon ( const WeaponType & _type, int _initialAmmo = 0 );

// ^ ссылка на дочерний объект приходит в конструкторе

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

Weapon ( const Weapon & ) = delete;

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

// Метод доступа к типу орудия

const WeaponType & getType () const;

// Метод доступа к количеству оставшихся зарядов

int getCurrentAmmo () const;

// Метод зарядки орудия. Возвращает количество оставшихся зарядов

int loadAmmo ( int _ammo );

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

bool hasFullAmmo () const;

bool hasNoAmmo () const;

// Метод имитации выстрела орудия

bool tryShoot ();

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

private:

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

// Ссылка на дочерний объект — тип орудия

const WeaponType & m_type;

// Количество оставшихся зарядов

int m_currentAmmo;

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

};

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

// Реализация метода доступа к типу орудия

inline const WeaponType &

Weapon::getType () const

{

return m_type;

}

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

// Реализация метода доступа к количеству оставшихся зарядов

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;

}