Narazil jsem na jeden návrhový problém. Vytvořil jsem si základní abstraktní třídu pro úsek trasy. Od této třídy dále dědím jednotlivé typy úseků (křivek). Tyto úseky mám uloženy v kontejneru, tak jak jdou za sebou - tvoří trasu.
Mezi jednotlivými úseky však potřebuji vytvořit přechody - s tím, že např. přechod mezi přímkou a kružnicí vypadá jinak než přechod mezi dvěma přímkami. Zatím mám ve třídě metodu, která přechod řeší a uvnitř ní mám switch podle typu navazujícího úseku.
Tento způsob se mi moc nepozdává - jak bylo řečeno na jedné přednášce "Pokud je třeba switchovat mezi typy odvozených tříd, je to známka špatného použití virtuálních metod". Jde ale řešit nějak jinak?
Nahlásit jako SPAM
IP: 85.160.35.–
Life is too short to remove USB mass storage safely...
Správný drsňák udělá z konzole cokoliv
vytvor si virtualnu triedu 'prechod', pripadne tzv functor alebo obycajny pointer na funkciu.
Mozno je problem to, ze samotne useky trasy mas ulozene v kontajnery, tym padom useky nevedia nic o svojich susedoch. Nebolo by lepsie nepouzit kontainer, ale priamo zlinkovt useky napr takto:
[UsekTrasy] -> [prepoj] -> [UsekTrasy] -> ....
Nahlásit jako SPAM
IP: 95.105.157.–
obfuscate: "The cruel god Malloc will strike you down. "
ZMeson: "That's the C god. C++ has a new god. "
Úseky o ostatních ví. Úseky mám uložené jako list<unique_ptr<PathPart>> path. Třída PathPart obsahuje vždy iterátor na sebe sama (úseky vždy budou uloženy v kontejneru a využívám vlastnosti listu, kdy iterátor zůstává platným nehledě na změnu ostatních prvků). Tedy najít další a předchozí třídu není problém.
Není mi jasné, jak funktor použít. Nemohl by jsi mi ukázat příklad? Dejme tomu, že mám odvozené třídy A a B. Každý z přechodů A->A, A->B, B->A a B->B je jinak definován (dá se říci, že v podstatě nemají vůbec nic společného). Jak vytvořím ten správný funktor pro správný typ přechodu?
Nahlásit jako SPAM
IP: 85.160.35.–
Life is too short to remove USB mass storage safely...
Správný drsňák udělá z konzole cokoliv
Ak bude druh prechodu zavysiet od zdrojoveho aj cielovaho prvka tak sa zrejme switchovniu nevyhnes, mozes to ale spravyt tak ze switch-ovat budes len pri vytvarani trasy.
Podla mna sa toto nehodi na pouzitie listu alebo nejakeho kontaineru a radsej by som Useky zlinkoval manualne najlepsie s medziprvkom ktory by mal na starosti zabezpecit spravny druh prechodu.
Nahlásit jako SPAM
IP: 95.105.157.–
obfuscate: "The cruel god Malloc will strike you down. "
ZMeson: "That's the C god. C++ has a new god. "
Díky za odpovědi. Takže skutečně nějaké elegantnější řešení než switch není.
Třídy ukládám do listu, protože ten za mě řeší memory managment. Nemusím programovat vlastní obdobu listu, která by ale stejně fungovala jenom pro jedne datový typ.
Nahlásit jako SPAM
IP: 85.160.35.–
Life is too short to remove USB mass storage safely...
Správný drsňák udělá z konzole cokoliv
Potom mam istotu ze je trieda alokovana na heape a zaroven mozem pouivat:
auto a = Trieda::New(/*...*/); //a auto je unique ptr
Ak nahodou potrebujem dynamicky alokovat data a mazat ich az na konci programu alebo su data priradene k uritemu objektu a po zruseni toho objektu uz niesu potrebne data tak na to mam specialny vector:
using std::move;
template <class T, class D = std::default_delete<T>>
class unique_ptr_vector : public std::vector<std::unique_ptr<T, D>>{
public:
typedef T* pointer;
typedef T element_type;
typedef D deleter_type;
typedef std::vector<std::unique_ptr<element_type, deleter_type>> container;
typedef std::unique_ptr<element_type, deleter_type> container_element;
protected:
public:
using typename container::iterator;
using typename container::reverse_iterator;
using typename container::const_iterator;
using typename container::const_reverse_iterator;
template <class TT, class DD>
inline void push_back(std::unique_ptr<TT, DD>&& p){
this->container::push_back(move(p));
}
template <class TT>
inline TT* push_back_new(TT* p){
this->container::push_back(std::unique_ptr<element_type, deleter_type>(p));
return p;
}
inline iterator release( iterator pos ){
pos->release();
return erase(pos);
}
inline iterator release( const_iterator pos ){
pos->release();
return erase(pos);
}
inline iterator release( iterator first, iterator last ){
for_each(first, last, [](container_element& i){
i.release();
});
return erase(first, last);
}
inline iterator release( const_iterator first, const_iterator last ){
for_each(first, last, [](container_element& i){
i.release();
});
return erase(first, last);
}
template <class UnaryPredicate>
void release_if(UnaryPredicate f){
iterator i = this->begin();
while(i != this->end()){
if(f(i->get())){
i->release();
iterator tmp = i;
++i;
this->erase(tmp);
}
else ++i;
}
}
void clear_release(){
for(auto& a : *this)a.release();
this->clear();
}
template <class TT>
bool contains(TT* p){
for(auto& a : *this)if(a.get() == p)return true;
return false;
}
inline void pop_back_release(){
this->back().release();
this->pop_back();
}
unique_ptr_vector(){}
template <class TT, class DD>
unique_ptr_vector(const unique_ptr_vector<TT, DD>&) = delete;
template <class TT, class DD>
unique_ptr_vector(unique_ptr_vector<TT, DD>&& u){
for(auto& a : u){
this->push_back(move(a));
}
u.clear();
}
template <class TT, class DD>
unique_ptr_vector& operator=(unique_ptr_vector<TT, DD>&& u){
this->clear();
for(auto& a : u){
this->push_back(move(a));
}
u.clear();
return *this;
}
template <class TT, class DD>
unique_ptr_vector& operator+=(unique_ptr_vector<TT, DD>&& u){
for(auto& a : u){
this->push_back(move(a));
}
u.clear();
return *this;
}
};
Nahlásit jako SPAM
IP: 95.105.157.–
obfuscate: "The cruel god Malloc will strike you down. "
ZMeson: "That's the C god. C++ has a new god. "
Mě se líbila elegantnost řešení s listem - je to téměř bez práce a navíc bez dalšího úsilí (na úpravu kódu) můžu automaticky začít používat všechny algoritmy z STL.
Nahlásit jako SPAM
IP: 85.160.35.–
Life is too short to remove USB mass storage safely...
Správný drsňák udělá z konzole cokoliv
Podle mě není problém v použití switch, ale v jeho umístění (tedy pokud správně chápu, že je uvnitř té třídy úseku). Přechod by neměl řešit úsek sám, ale problém by měl přenechat na řešení o úroveň výše, tj. vlastníkovi úseků, což bude nejspíše třída Trasa. Ta zná všechny třídy jednotlivých úseků a ví, jak se k nim chovat.
Pokud by si to měl ještě tak, že přechod si řeší třídy sami, tak přidáním dalšího úseku bys musel přidat řešení nového přechodu do všech tříd. Takhle s třídou Trasa to přidáváš jen na jedno místo.
Jinak pokud jsou ty přechody natolik rozdílné, že nelze najít "společnou řeč", tak není na switch nic špatného :).