Návrh třídy – C / C++ – Fórum – Programujte.com
 x   TIP: Přetáhni ikonu na hlavní panel pro připnutí webu

Návrh třídy – C / C++ – Fórum – Programujte.comNávrh třídy – C / C++ – Fórum – Programujte.com

 

Toto vlákno bylo označeno za vyřešené.
yaqwsx+9
Posthunter
17. 10. 2012   #1
-
0
-

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
vitamin+8
Grafoman
17. 10. 2012   #2
-
0
-

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. "
yaqwsx+9
Posthunter
17. 10. 2012   #3
-
0
-

Ú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
vitamin+8
Grafoman
17. 10. 2012   #4
-
0
-

   

class Usek{
	/*
	....
	*/
	protected:
		class Prechod{
			/*
			 * kazdy druh prechodu si zapameta ciel (kazda odvodena trieda od Prechod moze mat iny typ cielu)
			 */
		  public:
			  virtual ~Prechod(){}
			  virtual /*ret*/ operator () (/*parametre*/) = 0;
			  
		};
	private:
		Prechod* prechod;	
		
	public:
		void SetPrechod(Prechod* p){prechod = p;}
		virtual ~Usek(){}
};

edit:

Ale toto riesi len prechody A->A  a  A->B.

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. "
vitamin+8
Grafoman
17. 10. 2012   #5
-
0
-

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. "
yaqwsx+9
Posthunter
17. 10. 2012   #6
-
0
-

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
vitamin+8
Grafoman
17. 10. 2012   #7
-
0
-

Ved tie triedy mozes zlinkovat pomocou unique_ptr a nepotrebujes potom list.

Ja to u tried ktore nemaju byt alokovane na stacku riesim takto:

class Trieda{
	protected:
		Trieda(){}
		/*
		 * ... dalsie konstructory
		 */
	public:
		template <typename... ConArgs>
		static unique_ptr<Trieda> New(ConArgs... ca){
			return unique_ptr<Trieda>(new Trieda(ca...));
		}
};

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. "
yaqwsx+9
Posthunter
17. 10. 2012   #8
-
0
-

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
liborb
~ Redaktor
+18
Guru
18. 10. 2012   #9
-
0
-

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 :).

Nahlásit jako SPAM
IP: 188.75.135.–
Zjistit počet nových příspěvků

Přidej příspěvek

Toto téma je starší jak čtvrt roku – přidej svůj příspěvek jen tehdy, máš-li k tématu opravdu co říct!

Ano, opravdu chci reagovat → zobrazí formulář pro přidání příspěvku

×Vložení zdrojáku

×Vložení obrázku

Vložit URL obrázku Vybrat obrázek na disku
Vlož URL adresu obrázku:
Klikni a vyber obrázek z počítače:

×Vložení videa

Aktuálně jsou podporována videa ze serverů YouTube, Vimeo a Dailymotion.
×
 
Podporujeme Gravatara.
Zadej URL adresu Avatara (40 x 40 px) nebo emailovou adresu pro použití Gravatara.
Email nikam neukládáme, po získání Gravatara je zahozen.
-
Pravidla pro psaní příspěvků, používej diakritiku. ENTER pro nový odstavec, SHIFT + ENTER pro nový řádek.
Sledovat nové příspěvky (pouze pro přihlášené)
Sleduj vlákno a v případě přidání nového příspěvku o tom budeš vědět mezi prvními.
Reaguješ na příspěvek:

Uživatelé prohlížející si toto vlákno

Uživatelé on-line: 0 registrovaných, 47 hostů

Moderátoři diskuze

 

Hostujeme u Českého hostingu       ISSN 1801-1586       ⇡ Nahoru Webtea.cz logo © 20032024 Programujte.com
Zasadilo a pěstuje Webtea.cz, šéfredaktor Lukáš Churý