- •Ооп: Лекция 9. Композиция объектов.
- •Композиция объектов
- •Простейшая композиция по значению
- •Множественная композиция по значению
- •Ссылочная композиция — логическое объединение
- •Композиция с разрываемой связью
- •Разрываемая ссылочная композиция
- •Недетерминированная множественность композиции
- •Множественная композиция объектов-сущностей
- •Полные примеры из лекции
Ссылочная композиция — логическое объединение
Некоторые виды композиции не предполагают ответственности на уничтожение дочернего объекта. Такая связь скорее является логическим объединением в группу, нежели жестким отношением целое-часть. Для дочерних объектов в логических объединениях свойственно вхождение сразу в несколько родительских объектов. В таком случае за уничтожение дочернего объекта отвечает кто-либо другой, а не родительский объект.
Если такая связь между объектами является постоянной, то, как правило, дочерний объект хранится по ссылке либо константной ссылке, которая передается в родительский объект при конструировании. Обычно, таким видам объектов не свойственно копирование и присвоение, и эти операции имеет смысл в явном виде запретить.
Ниже представлено логическое объединение между орудием (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;
}
