Агрегация/композиция-указатели
Уже второй вопрос по данной теме,мне просто очень интересно.
Я если честно,не вижу отличий от композиции/агрегации,просто посмотрите следующий код:
#include<iostream>
#include <string>
class Humans;
class Spoons//Класс Ложки
{
private:
int serialNum;//часть(член)-серийный номер
public:
Spoons(int serial=0):serialNum(serial){}
int getSerial() { return serialNum; }
};
class Humans//класс Люди
{
private:
Spoons *spoons;//Люди "имеют" ложку, часть указывает на объект вне класса
//чтобы другие тоже могли пользоваться ложкой
Spoons m_spoon;
public:
Humans(Spoons spoon) :m_spoon(spoon){}
Humans(Spoons *spoon1):spoons(spoon1){}
Spoons getSpoon() { return m_spoon; }
Spoons getSp() { return *spoons; }
friend std::ostream& operator<<(std::ostream &out, Humans spoon);
friend std::ostream& operator<<(std::ostream &out, Humans &spoon1);
};
std::ostream& operator<<(std::ostream &out, Humans spoon)
{
out << spoon.m_spoon.getSerial()<<std::endl;
return out;
}
std::ostream& operator<<(std::ostream &out, Humans &spoon1)
{
out << spoon1.spoons->getSerial() << std::endl;
return out;
}
int main()
{
Spoons spoon(204);
Spoons *fork = new Spoons(208);//Выделяем память
{//просто область видимости
Humans human(spoon);//Композиция
Humans human1(&spoon);//Агрегация
Humans person(fork);
}//конец
Humans dad(spoon);//Композиция
std::cout<<dad.getSpoon();
Humans mom(&spoon);//Агрегация
std::cout << mom.getSp();
Humans bro(fork);//Также Агрегация
std::cout << bro.getSp();
}
Вывод:dad-204,mom-204,bro-208
В чем заключается вопрос.В чем смысл использовать указатели(а именно агрегацию),если и по значению(с композицией),и с указателями(агрегацией) можно любому количеству объектов присваивать объект spoon.Посмотрите в main-е.Хотя:
Для реализации композиции объект и часть должны иметь следующие отношения:
Часть (член) является частью объекта (класса).
Часть (член) может принадлежать только одному объекту (классу) в моменте.
Часть (член) существует, управляемая объектом (классом).
Часть (член) не знает о существовании объекта (класса).
Для реализации агрегации целое и его части должны соответствовать следующим отношениям:
Часть (член) является частью целого (класса).
Часть (член) может принадлежать более чем одному целому (классу) в моменте.
Часть (член) существует, не управляемая целым (классом).
Часть (член) не знает о существовании целого (класса).
Но я объект spoon присвоил 2-ум композициям и 2-ум агрегациям,также еще 2-ум агрегациям объект fork.Тогда в чем польза от указателей??? Если в итоге это можно сделать по значению?(с помощью композиции??).Или я что-то не понимаю??Объясните,пожалуйста!!!!
Ответы (2 шт):
Композиция и агрегация являются терминами определенных отношений между объектами и классами в ООП.
Указатель и агрегация это разные понятие, но имеют связь по своей реализации.
Указатель – переменная, значением которой является адрес ячейки памяти.
Разрабатывая какие-то маленькие программы вы никогда не задумаетесь о потреблении ресурсов программой. Разрабатывая большие программы, которые используют много объектов, потребляют много памяти.
В далеких 90-х, да в принципе еще и в 60-х и по сей день, это остается проблемой, т.е. проблема в ограниченных ресурсах устройства, на котором работает программа.
Чтобы уйти от проблемы, вместо инициализации, например, 1000 новых идентичных объектов начали использовать указатели, которые позволяют использовать 1 объект в разных местах программы (речь про объект, а не про класс, обратите внимание). Это позволило сократить занимаемые ресурсы программы в 1000 раз.
В итоге, мы 1 раз инициализировали объект и везде его использовали, и в этом нам помогла ссылка на память, т.е. указатель.
Там, где программе не нужна необходимость использовать один идентичный объект, там мы будем создавать разные независящие объекты. Эти объекты, могу в свою очередь, создавать свои связанные объекты, которыми будет управлять только этот объект, и при удалении первого объекта, эти объекты не прекратят свое существование.
Пример. На всю игру есть автомат и нож - оружие. Есть 2 игрока (один класс). Игроки имеют руки, ноги, глаза (это тоже все отдельные классы). Оружием может владеть один игрок или им может никто не владеть.
Во время игры будут инициализированы два игрока, у которых будут руки, ноги, голова. Мы их сделали так, чтобы они принадлежали своим родителям (игрокам). При удалении одного из объектов будут удалены именно его части тела. Но в отличие от реальной жизни, мы можем поступить иначе и ниже это описано.
Игрок может использовать оружие. Допустим один из игроков взял оружие. Мы не будем инициализировать у игрока оружие, т.к. это накладывает расходы на память, но это дело второе, а дело первое, мы даем объекту возможность использовать разное оружие не расширяя класс. Т.е. мы можем создавать новые классы оружия с единым интерфейсом не изменяя класс игрока, например, добавляя методы "новый нож", "новый автомат", "новая гранат".
Так вот, мы дали ему ссылку на объект оружие, чтобы заново не инициализировать объект. Взяв ссылку на оружие, мы дали оружию владельца "игрока", чтобы наше оружие принадлежало только одному владельцу. На самом деле оружию все равно кто им владеет, это тонкости реализации.
Игрок 1 использовал оружие, например, пострелял и потратил патроны.
Допустим, игрок выбросил свое оружие. Объект игрока снова принял ссылку на объект оружия, которое нужно скинуть и выполним определенные действия, и оружие и игрок потеряли на него ссылку.
Т.к. оружие у нас независимое, оно может иметь какой-то расход (количество патронов, например), а может быть неограниченным.
Оружие взял другой игрок. Количество патронов в оружии было изменено первым игроком.
Игрок 2 пострелял и выбросил оружие.
Мы хранили все данные, связанные с оружием, в объекте оружия и не давали знаний этих данных их владельцам, точнее мы не вынуждали их хранить. Однако мы можем давать знать о наличие остаточного ресурса игроку или геймплею.
Мы использовали один объект, одну ссылку, которую связывали поочередно с разными игроками.
Конечно, ничего в реализации не мешает нам сделать так, чтобы этот автомат использовали одновременно 2 игрока, но и количество патронов будет одно на двоих, если мы будем хранить их в объекте оружия.
Теперь о игроках, которые имеют свои руки, ноги, головы.
Мы создавали для этих целей разные объекты, для каждого игрока. Хотя, с точки зрения ресурсов и внешнего облика персонажей, нам нет смысла тратить расходы на эти объекты. Это конечно зависит от логики персонажей, как раз о том, о чем я выше говорил, что отличает наш мир, о кода.
Мы можем использовать один из сценариев:
- Части тела принадлежат одному персонажу. Мы можем их оторвать (удалить из родительского объекта) и игрок станет без этой части. Каждая рука может обладать определенной силой, если это предусматривает наш сценарий. Т.е. каждый игрок может иметь разную "силу кулака" и даже увеличивать ее.
- Мы можем не разрабатывать логику силы из пункта 1, но оторвать руку мы все равно можем.
Если с первым сценарием все понятно, то во втором мы можем "схитрить" над частями тела. Как?
Мы используем для наших персонажей одинаковые части тела, т.е. одинаковые ссылки на объекты. Т.е. у каждого персонажа будут использоваться, с т.з. памяти, одинаковые ресурсы.
Как это сделать? Мы можем скрыть инстанцирование частей тела от класса, используя для этого фабрику (погружение в паттерны). Например, вместо new Hand, мы вызовем factory->makeHand() и получим нужную нам ссылку на единый объект.
Зачем так? Уже выше описывал, чтобы сократить количество ресурсов устройства. Да, два игрока это мало, но представьте себе если их тысячи, а логикой игры все равно не затрагиваются части тела.
В распоряжении игрока все равно остается возможность "оторвать" часть тела, просто удалив ссылку на объект, но объект продолжил существовать у другого персонажа.
Всё стало понятно,просто нужно смотреть на вещи просто.
int main()
{
Spoons spoon(204);
Spoons *fork = new Spoons(208);
{//просто область видимости
Humans human(spoon);
Humans human1(&spoon);
Humans person(fork);
}//конец
Humans dad(spoon);
std::cout<<dad.getSpoon();
Humans mom(&spoon);
std::cout << mom.getSp();
Humans bro(fork);
std::cout << bro.getSp();
}
Всё вышеприведенное-это агрегация ,так как члены-данных(части):указатель на объектSpoons *spoons и объект Spoons m_spoon по сути,принимают в качестве параметра объект с которым"связываются",поэтому член и в виде указателя на объект,и в виде объекта:
Часть (член) может принадлежать более чем одному целому (классу) в моменте.
Как в вышеприведенном коде.
Композицией же будет что-то такое:
Humans man(Spoons(212));
Здесь же член принадлежит только одному объекту и жестко инкапсулирован в него+не может принадлежать еще кому-то,и после выхода из области видимости(любой) уничтожится вместе с классом.