Comments 42
Linked list состоящий из 2х элементов?
+10
Не силён в сях, но по-моему вы изобрели медиатор.
+2
Приведите пожалуйста пример практического применения.
+2
Например, в какой-нибудь игре могли бы быть такие связи: «игровой персонаж» (Player) — «транспортное средство» (Vehicle) и «транспортное средство» (Vehicle) — «пушка» (Weapon).
Что можно было бы реализовать с использованием представленного шаблона так:
А дальше:
1. Сел игрок-1 в машину-1:
2. Поставил на машину-1 пушку-1:
3. Потом пересел из машины-1 в машину-2:
…
Что можно было бы реализовать с использованием представленного шаблона так:
class Player: public O<Player, Vehicle> {};
class Vehicle: public O<Vehicle, Player>, public O<Vehicle, Weapon> {};
class Weapon: public O<Weapon, Vehicle> {};
А дальше:
1. Сел игрок-1 в машину-1:
p1->SetLink(v1);
2. Поставил на машину-1 пушку-1:
p1->GetLink<Vehicle>()->SetLink(w1);
3. Потом пересел из машины-1 в машину-2:
p1->SetLink(v2);
…
+3
Спасибо.
0
Я бы так не делал.
Первое — я бы использовал умные указатели.
Второе — делал бы методы с говорящими именами. player->SitInCar( car ); К тому же в этом случае легко реализовать дополнительную логику.
Первое — я бы использовал умные указатели.
Второе — делал бы методы с говорящими именами. player->SitInCar( car ); К тому же в этом случае легко реализовать дополнительную логику.
+3
> 1. Сел игрок-1 в машину-1:
> p1->SetLink(v1);
> 2. Поставил на машину-1 пушку-1:
> p1->GetLink()->SetLink(w1);
И автоматом вылез из машины. Извините, место одно — или игрок в машине, или пушка :)
Я не хочу сказать, что вашей связи нет применения, но в случае с игрой действительно обычно не 1 связь у объекта. Нужно поискать более практичный пример. Мне кажется, что это слишком узкоспециализированный шаблон (и при этом достаточно простой) чтоб его описывать в книгах.
> p1->SetLink(v1);
> 2. Поставил на машину-1 пушку-1:
> p1->GetLink()->SetLink(w1);
И автоматом вылез из машины. Извините, место одно — или игрок в машине, или пушка :)
Я не хочу сказать, что вашей связи нет применения, но в случае с игрой действительно обычно не 1 связь у объекта. Нужно поискать более практичный пример. Мне кажется, что это слишком узкоспециализированный шаблон (и при этом достаточно простой) чтоб его описывать в книгах.
0
Прошу прощения, про v.4 (множественные ссылки) не прочитал.
Тем не менее, множественное наследование не слишком практично в использовании. Ну тот же пример с игрой, но в машину может сесть 4 человека, а не 1.
Тем не менее, множественное наследование не слишком практично в использовании. Ну тот же пример с игрой, но в машину может сесть 4 человека, а не 1.
0
но в машину может сесть 4 человека, а не 1
Вопросов нет, данный шаблон рассчитан только на связи 1 к 1.
0
Глазом моргнуть не успеешь, как он должен будет быть на это рассчитан ) Причём «вчера»
Я высказал своё мнение по-подробнее в этом комментарии
Я высказал своё мнение по-подробнее в этом комментарии
0
Как же в этой связи мне нравится Qt.
Скажем, если человеку нужно завести несколько собак, то приведённый код надо переписывать с нуля. А если у каждой собаки может быть несколько владельцев одновременно, то слежение за всеми ссылками превращается в сущий кошмар.
А в Qt если необходимо установить связь между объектами, совсем не обязательно в объекте хранить указатели на соседа. Например:
Соответственно, если humanObject или dogObject удалить, то связь будет разрушена и методы вызываться не будут.
Скажем, если человеку нужно завести несколько собак, то приведённый код надо переписывать с нуля. А если у каждой собаки может быть несколько владельцев одновременно, то слежение за всеми ссылками превращается в сущий кошмар.
А в Qt если необходимо установить связь между объектами, совсем не обязательно в объекте хранить указатели на соседа. Например:
connect(humanObject, SIGNAL(giveCandies(int)), dogObject, SLOT(eatCandies(int)));
connect(dogObject, SIGNAL(bark()), humanObject, SLOT(runAway()));
Соответственно, если humanObject или dogObject удалить, то связь будет разрушена и методы вызываться не будут.
+3
И как получить здесь получить всех собак человека?
+2
Если кратко, то никак. А зачем в определённый момент может понадобиться список всех собак? Если для того, чтобы выполнить какой-нибудь метод или вызвать деструктор, то сигналы-слоты с этим нормально справляются. Всю логику отношений N к N можно выразить таким образом.
0
connect у нас совсем не хранит указатели Оо
0
Реализовывал нечно подобное, но на макросах и с указанием названия методов для более читабельного API. Очень удобный подход.
0
>>И тут мне подумалось: «надо это переделать на шаблоны!»
Нужно как-то себя ловить на этой мысли и пытаться сдерживать до появления действительно веских причин для использования шаблонов :)
А так, забавно, но не более того…
Нужно как-то себя ловить на этой мысли и пытаться сдерживать до появления действительно веских причин для использования шаблонов :)
А так, забавно, но не более того…
0
h1->SetLink(h2)
BDSM?
+10
Для перегрузки методов родительских классов «внутри» потомка при множественном наследовании достаточно ввести имена методов родительских классов в class scope класса-потомка.
class Human: public O<Human, Dog>, O<Human, Cat>
{
public:
using O<Human, Dog>::Setlink;
using O<Human, Cat>::Setlink;
};
0
один человек может владеть максимум одной собакой/кошкой/сепулькой..?
0
«Класс В есть наследник класса А» означает, что всякий В является одновременно и А.
Всякий человек является поводком?)
Всякий человек является поводком?)
-1
Согласен, принцип «всякое наследование — это отношение вида „является“» здесь некоторым образом игнорируется.
Но можно и перефразировать: «всякий человек является потенциальным хозяином/соседом собаки/сепульки».
В принципе, у меня был вариант реализации данной схемы не через наследование, а через композицию, когда экземпляр O<Human, Dog> явлется членом класса Human. Но в этом случае усложняется код инициализации — в конструкторе класса Human приходится инициализировать объект O<Human, Dog> указателем this, и усложняется «пользовательский» код — вместо
Но можно и перефразировать: «всякий человек является потенциальным хозяином/соседом собаки/сепульки».
В принципе, у меня был вариант реализации данной схемы не через наследование, а через композицию, когда экземпляр O<Human, Dog> явлется членом класса Human. Но в этом случае усложняется код инициализации — в конструкторе класса Human приходится инициализировать объект O<Human, Dog> указателем this, и усложняется «пользовательский» код — вместо
h1->SetLink(d1);
надо писать h1->GetLinker()->SetLink(d1);
.+1
UFO just landed and posted this here
На паттерн проектирования не тянет всё-таки IMHO, но в качестве практических занятий с шаблонами в С++ покатит :)
+4
Идея зацепила, пока не знаю к чему бы применить, но, вспоминая труды Александреску :) пришло в голову некоторое обобщенное решение с применением списков типов.
В Списке типа можно указать все типы, с которыми мы хотим связываться, в том числе и с собой.
Данное решение писалось под MSVC++ 2005, доберусь до gcc и там проверю. Зачастую бывает писанное в студии с первого раза не компилится в gcc.
Данное решение не проверяет если пользователь в один список типов включил несколько одинаковых типов.
В общем если кому интерсно, то вот еще + 1 вариант к предложенному автором.
В Списке типа можно указать все типы, с которыми мы хотим связываться, в том числе и с собой.
Данное решение писалось под MSVC++ 2005, доберусь до gcc и там проверю. Зачастую бывает писанное в студии с первого раза не компилится в gcc.
Данное решение не проверяет если пользователь в один список типов включил несколько одинаковых типов.
В общем если кому интерсно, то вот еще + 1 вариант к предложенному автором.
#include <iostream>
#include <vector>
#include <algorithm>
template <typename H, typename T>
struct TypeList
{
typedef H Head;
typedef T Tail;
};
struct NullType
{
};
#define TYPE_LIST_1(t1) \
TypeList<t1, NullType>
#define TYPE_LIST_2(t1, t2) \
TypeList<t1, TYPE_LIST_1(t2) >
#define TYPE_LIST_3(t1, t2, t3) \
TypeList<t1, TYPE_LIST_2(t2, t3) >
#define TYPE_LIST_4(t1, t2, t3, t4) \
TypeList<t1, TYPE_LIST_3(t2, t3, t4) >
#define TYPE_LIST_5(t1, t2, t3, t4, t5) \
TypeList<t1, TYPE_LIST_4(t2, t3, t4, t5) >
template <typename T>
struct TypeWrap
{
};
template <typename TClass, typename TLinkedClasses>
class LinkHolder
: public LinkHolder<TClass, typename TLinkedClasses::Tail>
{
public:
typedef typename TLinkedClasses::Head LinkType;
virtual ~LinkHolder()
{
LinkPool Tmp = Links;
for (LinkPool::const_iterator i = Tmp.begin() ; i != Tmp.end() ; ++i)
(*i)->DelLink(static_cast<TClass *>(this));
}
using LinkHolder<TClass, typename TLinkedClasses::Tail>::AddLink;
using LinkHolder<TClass, typename TLinkedClasses::Tail>::DelLink;
void AddLink(LinkType *link)
{
if (std::find(Links.begin(), Links.end(), link) != Links.end())
return;
Links.push_back(link);
link->AddLink(static_cast<TClass *>(this));
}
void DelLink(LinkType *link)
{
LinkPool::iterator Iter = std::find(Links.begin(), Links.end(), link);
if (Iter == Links.end())
return;
LinkType *Link = *Iter;
Links.erase(Iter);
Link->DelLink(static_cast<TClass *>(this));
}
template <typename T>
unsigned GetLinkCount() const
{
return GetLinkCountImpl(TypeWrap<T>());
}
template <typename T>
T* GetLink(unsigned index) const
{
return GetLinkImpl(TypeWrap<T>(), index);
}
protected:
using LinkHolder<TClass, typename TLinkedClasses::Tail>::GetLinkCountImpl;
unsigned GetLinkCountImpl(const TypeWrap<LinkType> &) const
{
return static_cast<unsigned>(Links.size());
}
using LinkHolder<TClass, typename TLinkedClasses::Tail>::GetLinkImpl;
LinkType* GetLinkImpl(const TypeWrap<LinkType> &, unsigned index) const
{
return Links.size() > index ? Links[index] : 0;
}
private:
typedef std::vector<LinkType *> LinkPool;
LinkPool Links;
};
template <typename TClass>
class LinkHolder<TClass, NullType>
{
public:
virtual ~LinkHolder()
{
}
void AddLink()
{
}
void DelLink()
{
}
unsigned GetLinkCountImpl(const TypeWrap<NullType> &) const
{
return -1;
}
NullType* GetLinkImpl(const TypeWrap<NullType> &, unsigned) const
{
return 0;
}
};
class A;
class B;
class C;
class A
: public LinkHolder<A, TYPE_LIST_3(A, B, C)>
{
public:
void A2B();
void Stop();
};
class B
: public LinkHolder<B, TYPE_LIST_3(A, B, C)>
{
public:
void B2C();
};
class C
: public LinkHolder<C, TYPE_LIST_3(A, B, C)>
{
public:
void C2A();
};
void A::A2B()
{
std::cout << __FUNCTION__ << std::endl;
unsigned Count = GetLinkCount<B>();
for (unsigned i = 0 ; i < Count ; ++i)
GetLink<B>(i)->B2C();
}
void A::Stop()
{
std::cout << __FUNCTION__ << std::endl;
}
void B::B2C()
{
std::cout << __FUNCTION__ << std::endl;
unsigned Count = GetLinkCount<C>();
for (unsigned i = 0 ; i < Count ; ++i)
GetLink<C>(i)->C2A();
}
void C::C2A()
{
std::cout << __FUNCTION__ << std::endl;
unsigned Count = GetLinkCount<A>();
for (unsigned i = 0 ; i < Count ; ++i)
GetLink<A>(i)->Stop();
}
int main()
{
A a;
std::auto_ptr<B> b(new B);
C c;
a.AddLink(&a);
a.AddLink(b.get());
a.AddLink(&c);
b->AddLink(&a);
b->AddLink(b.get());
b->AddLink(&c);
c.AddLink(&a);
c.AddLink(b.get());
c.AddLink(&c);
a.A2B();
b.reset(0);
std::cout << std::endl;
a.A2B();
}
+1
Отличное обобщение!
Чтобы это собрать в gcc нужно применить такой diff:
Чтобы это собрать в gcc нужно применить такой diff:
--- a/typelists.cpp
+++ b/typelists.cpp
@@ -2,6 +2,7 @@
#include <vector>
#include <algorithm>
+#include <memory>
template <typename H, typename T>
struct TypeList
@@ -43,7 +44,7 @@ public:
virtual ~LinkHolder()
{
LinkPool Tmp = Links;
- for (LinkPool::const_iterator i = Tmp.begin() ; i != Tmp.end() ; ++i)
+ for (typename LinkPool::const_iterator i = Tmp.begin() ; i != Tmp.end() ; ++i)
(*i)->DelLink(static_cast<TClass *>(this));
}
using LinkHolder<TClass, typename TLinkedClasses::Tail>::AddLink;
(*i)->DelLink(static_cast<TClass *>(this));
}
using LinkHolder<TClass, typename TLinkedClasses::Tail>::AddLink;
@@ -57,7 +58,7 @@ public:
}
void DelLink(LinkType *link)
{
- LinkPool::iterator Iter = std::find(Links.begin(), Links.end(), link);
+ typename LinkPool::iterator Iter = std::find(Links.begin(), Links.end(), link);
if (Iter == Links.end())
return;
LinkType *Link = *Iter;
+1
Лучше бы сделать у этого шаблона виртуальный деструктор, раз вы от него наследуетесь.
0
Справедливое замечание.
Но это будет необходимо только в случае, если где-то в коде я буду держать полиморфный указатель O<Human,Dog>* (или другой базовый класс), а потом захочу его уничтожить. Но пока я не могу придумать случаев, когда вместо просто Human* я хотел бы использовать именно O<Human, что-то>*.
Но это будет необходимо только в случае, если где-то в коде я буду держать полиморфный указатель O<Human,Dog>* (или другой базовый класс), а потом захочу его уничтожить. Но пока я не могу придумать случаев, когда вместо просто Human* я хотел бы использовать именно O<Human, что-то>*.
0
Мне не нравится данное решение, потому что мне не нравится сам подход хранения ссылок друг на друга. Это типичное отношение многие-ко-многим (человек может иметь много домашних животных и домашнее животное может иметь много хозяев). В реляционных БД в таких случаях заводится третья таблица и отношение многие-ко-многим сводится к двум отношениям один-ко-многим. В реальности отношение человека с домашним животным является внешней характеристикой по отношению к ним обоим: нельзя сказать, что наличие/отсутствие домашнего животного это неотъемлемая характеристика человека. Наличие животного это не цвет глаз. Может ещё ссылку на перочинный ножик в Human положить? Класс Person/Human/Whatever не должен иметь поля, указывающего на животных или указывающего на отсутствие животных.
Я думаю, что ваш подход ещё вызовет у вас головную боль и предлагаю одуматься:
Я думаю, что ваш подход ещё вызовет у вас головную боль и предлагаю одуматься:
class Person
{
};
class Pet
{
};
class PetOwnerRelations
{
public:
vector<Pet*> petsOfOwner(Person* owner);
vector<Person*> ownersOfPet(Pet* pet);
};
+2
Касательно многие-ко-многим мы тут уже обсудили выше. Да, эта реализация поддерживает только 1:1 by design. Связи типа 1:1 в нашем с вами мире так же реальны как и M:M. Возможно, пример с питомцами не очень удачен.
С другой стороны — вариант для M:M (M != const) предложен выше. Лично мне ещё интересен вариант M:M, где M == const.
Внешнее хранение ссылок мне в голову тоже приходило. Но даже в этом случае придется модифицировать классы Person и Pet — как минимум придется добавить в деструктор каждого что-то вроде
С другой стороны, если PetOwnerRelations сделать шаблонным — может выйти вполне универсальное решение. Другое дело, что тогда для связи между каждой парой типов придется заводить отдельный экземпляр соответствующего класса XRelations. Возможно, в некоторых случаях это будет лучшее решение, но я не уверен, что во всех.
С другой стороны — вариант для M:M (M != const) предложен выше. Лично мне ещё интересен вариант M:M, где M == const.
Внешнее хранение ссылок мне в голову тоже приходило. Но даже в этом случае придется модифицировать классы Person и Pet — как минимум придется добавить в деструктор каждого что-то вроде
PetOwnerRelations::Remove(this);
. Так что совсем внешней реализации связи, обладающей описанными в статье свойствами, не получается.С другой стороны, если PetOwnerRelations сделать шаблонным — может выйти вполне универсальное решение. Другое дело, что тогда для связи между каждой парой типов придется заводить отдельный экземпляр соответствующего класса XRelations. Возможно, в некоторых случаях это будет лучшее решение, но я не уверен, что во всех.
0
Sign up to leave a comment.
С++: шаблон «поводок»