Dědičnost – C / C++ – Fórum – Programujte.com
 x   TIP: Přetáhni ikonu na hlavní panel pro připnutí webu

Dědičnost – C / C++ – Fórum – Programujte.comDědičnost – C / C++ – Fórum – Programujte.com

 

Bobek
~ Anonymní uživatel
14 příspěvků
12. 1. 2016   #1
-
0
-

Ahoj mám jednu otázku, která mi vrtá hlavou.

Popíšu příklad: 

class A
{
protected:
   virtual void run() = 0;
}

class B : public A
{
   // other methods
}

class C
{
   void run{};
}

void function (B *b)
{
    b->run();
}

int main()
{
   C *c = new B;
   
   function(c);
	
   return 0;
}

jak je možné, že toto zafunguje?

Nahlásit jako SPAM
IP: 213.250.231.–
Kit+15
Guru
12. 1. 2016   #2
-
0
-

#1 Bobek
Zkus si místo nesmyslů A, B, C vložit nějaká slova ze života. Například A = Zvíře, B = Pes, C = Auto.

Když jsem si to udělal pro sebe, tak mi z toho vylezlo 

Auto *auto = new Pes;

což je skutečně nesmysl a také na to čumím, jak je to možné.

Nahlásit jako SPAM
IP: 194.228.13.–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
KIIV
~ Moderátor
+43
God of flame
12. 1. 2016   #3
-
+1
-
Zajímavé
Kit +

   

test.cpp:5:1: error: expected ‘;’ after class definition
 }
 ^
test.cpp:10:1: error: expected ‘;’ after class definition
 }
 ^
test.cpp:14:13: error: variable or field ‘run’ declared void
    void run{};
             ^
test.cpp:15:1: error: expected ‘;’ after class definition
 }
 ^
test.cpp: In function ‘void function(B*)’:
test.cpp:4:17: error: ‘virtual void A::run()’ is protected
    virtual void run() = 0;
                 ^
test.cpp:19:12: error: within this context
     b->run();
            ^
test.cpp: In function ‘int main()’:
test.cpp:24:15: error: invalid new-expression of abstract class type ‘B’
    C *c = new B;
               ^
test.cpp:7:7: note:   because the following virtual functions are pure within ‘B’:
 class B : public A
       ^
test.cpp:4:17: note:    virtual void A::run()
    virtual void run() = 0;
                 ^
test.cpp:26:14: error: cannot convert ‘C*’ to ‘B*’ for argument ‘1’ to ‘void function(B*)’
    function(c);
              ^


No moc funkcni zdrojak to ani zdaleka neni

Nahlásit jako SPAM
IP: 94.113.92.–
Program vždy dělá to co naprogramujete, ne to co chcete...
KIIV
~ Moderátor
+43
God of flame
12. 1. 2016   #4
-
+1
-
Zajímavé
Kit +

A kdyz se to trochu poupravuje, aby to aspon castecne davalo smysl:

test.cpp: In function ‘int main()’:
test.cpp:27:15: error: cannot convert ‘B*’ to ‘C*’ in initialization
    C *c = new B;
               ^
test.cpp:29:14: error: cannot convert ‘C*’ to ‘B*’ for argument ‘1’ to ‘void function(B*)’
    function(c);
Nahlásit jako SPAM
IP: 94.113.92.–
Program vždy dělá to co naprogramujete, ne to co chcete...
Kit+15
Guru
12. 1. 2016   #5
-
0
-

#4 KIIV
Dopadl jsem úplně stejně. Nechápu, jak někomu ten první zápis mohl projít.

Nahlásit jako SPAM
IP: 194.228.13.–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
KIIV
~ Moderátor
+43
God of flame
12. 1. 2016   #6
-
0
-

#5 Kit
mozna nejaky ultra stary kompilator.. Ale nejsme v indii, aby se to tu delalo treba na TurboC

Nahlásit jako SPAM
IP: 94.113.92.–
Program vždy dělá to co naprogramujete, ne to co chcete...
PiranhaGreg0
Stálý člen
13. 1. 2016   #7
-
0
-

#2 Kit
Tak zrovna s tím přiřazením Psa do Auta moc problém nevidím. Je to ukazatel na ukazatel. Jen to případně přetypovat, jinak se to kompilátoru moc nelíbí.

#include <iostream>

using namespace std;

class Animal {
public:
	void run() {
		cout << "Hello from Animal" << endl;
	}
};

class Car {
	void run() {
		cout << "Hello from Car" << endl;
	}
};

void function(Animal * x) {
	x->run();
}

int main() {
	Car * c = (Car *)new Animal;

	function((Animal *)c);

	return 0;
}

Co je spíše v úvodním kódu špatně, tak předpoklad, že se virtuální metoda run namapuje na tu ze třídy C. Virtuální metody se ukládají jinak než ty statické a tak při zavolání metody run ve funkci function dojde k nějakýmu nesmyslu.

Dnešní kompilátor tě pak dále ještě sprdne, že třída B je abstraktní (protože nepřepisuješ tu metodu run) a nemůžeš tak ani vytvořit instanci třídy...

Nahlásit jako SPAM
IP: 147.32.126.–
Kit+15
Guru
13. 1. 2016   #8
-
0
-

#7 PiranhaGreg
Vždycky je nejlepší pojmenovat třídy podle aktuálně řešeného problému, který ovšem v tomto případě neznáme. Už jsem alergický na všechny ty Foo a Bar, které o řešeném problému neříkají vůbec nic a ani se na nich nedá nic vysvětlit. OOP totiž vůbec není o syntaxi, ale o sémantice. Názvy tříd a objektů jsou mnohem důležitější než klíčová slova, která slouží jen jako lepidlo.

Přetypování je jen berličkou pro ty, kteří řešení problému nezvládli. Podobným nesmyslem je například operátor instanceof, který bývá tam, kde programátor nepochopil polymorfismus.

Nahlásit jako SPAM
IP: 194.228.13.–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
PiranhaGreg0
Stálý člen
13. 1. 2016   #9
-
0
-

#8 Kit
Nemůžu než souhlasit  

Nahlásit jako SPAM
IP: 147.32.126.–
KIIV
~ Moderátor
+43
God of flame
13. 1. 2016   #10
-
+1
-
Zajímavé
Kit +

#7 PiranhaGreg
prirazeni psa do auta neni problem, problem je v tom, ze rikas "tento pes je auto..."

Nahlásit jako SPAM
IP: 62.168.56.–
Program vždy dělá to co naprogramujete, ne to co chcete...
ondrej39+1
Věrný člen
13. 1. 2016   #11
-
0
-

#8 Kit
Pokud se rozebírá určité chování jazyka, je úplně jedno jak se třídy, proměnné a metody nazývají. Nebudu vymýšlet komplexní problém, když pro jednoduchou demonstraci bohatě stačí názvy typu Foo a Bar.

Pokud se chceš zabývat správností návrhu, pak jsou vhodné reálné názvy, v případě, kdy řešíš jazyk samotný ti může být úplně ukradené, jak se co jmenuje, a na čitelnost kódu to nemá absolutně žádný vliv.

Nahlásit jako SPAM
IP: 79.141.243.–
Inject all the dependencies!
PiranhaGreg0
Stálý člen
13. 1. 2016   #12
-
0
-

#10 KIIV
Vím, vím... Však ono kdykoliv se v C++ objeví nějakej cast, měl by se člověk zamyslet, jestli není něco špatně (někdy není).

Nahlásit jako SPAM
IP: 147.32.126.–
13. 1. 2016   #13
-
+1
-
Zajímavé
Kit +

#11 ondrej39
"lidsky" srozumitelnější jsou příklady zvíře - pes, auto - náklaďák než A, B nebo Foo. Použití příliš abstraktních pojmů pak často vede k nepochopení byť jednoduchých věcí. Pak je snadné smíchat jabka s hruškama a chtít je koupit v autoservisu, protože si pod abstraktním označením nikdo nedovede představit, že se jedná o objekty představující neslučitelné věci.

hu

Nahlásit jako SPAM
IP: 195.178.67.–
Kit+15
Guru
13. 1. 2016   #14
-
0
-

#11 ondrej39
KIIV napsal větu "tento pes je auto...", která je evidenním nesmyslem. Pokud by použil původní názvy, napsal by "Instancí třídy B je objekt třídy C", jehož nesmyslnost dokážeme teprve když se rozhlédneme po okolních řádcích, definici tříd nebo komentářích.

Účelem vhodně volených názvů je omezení zkoumání okolí zápisu na minimum tak, aby i samotný zápis byl srozumitelný na první pohled.

Použití názvů tříd A, B, C, Foo nebo Bar považuji za obfuskaci.

Nahlásit jako SPAM
IP: 194.228.13.–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
ondrej39+1
Věrný člen
13. 1. 2016   #15
-
0
-

#13 hlucheucho
Ono je ovšem úplně jedno, co je nebo není neslučitelné, dokud opravdu neřešíš nějaký reálný problém, což OP neřeší. Zajímá ho funčknost jazyka samotného a jak je možné, že by kód jemu podobný vůbec prošel kompilací (v jeho případě by bohužel neprošel, na tom nezáleží).

#1 Bobek
Možné to není, tvůj kód jednoduše fungovat nebude.

Nahlásit jako SPAM
IP: 79.141.243.–
Inject all the dependencies!
Kit+15
Guru
13. 1. 2016   #16
-
0
-

#15 ondrej39
Na to, abych ten problém mohl pochopit, musel jsem provést zmíněnou deobfuskaci. Nemám takovou představivost, abych pochopil, že v daném kódu "B je potomkem A", protože obecně platí, že "B není potomkem A".

Nahlásit jako SPAM
IP: 194.228.13.–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
ondrej39+1
Věrný člen
13. 1. 2016   #17
-
0
-

#1 Bobek
Dodatek:

Kromě toho, pokud třída B uvede nové metody, které třída A nemá, pak porušuješ princip substituce Barbary Liskovové, který říká:

A type hierarchy is composed of subtypes and supertypes. The intuitive idea of a subtype is one whose objects provide all the behavior of objects of another type (the supertype) plus something extra. What is wanted here is something like the following substitution property [6]: If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T. (See also [2], [17] for other work in this area.)
 

Z čehož je logické, přidávání metod do zděděných tříd absolutně nemá smysl, protože nikdy nebudou v aplikaci dostupné. Pokud nutně něco takového potřebuješ, měl by ses vydat raději cestou kompozice před dědičností a udělat si dekorátor na třídu A, který bude třídu A přebírat jako závislost.

Nahlásit jako SPAM
IP: 79.141.243.–
Inject all the dependencies!
Kit+15
Guru
13. 1. 2016   #18
-
0
-

#17 ondrej39
Trochu to zkonkretizuji (deobfuskuji): Pokud do třídy Pes přidám metodu štěkej(), bude to porušením LSP. Po regulérním přiřazení:

Zvíře *zvíře = new Pes();

není možné regulérně zavolat metodu zvíře->štěkej(), protože zvířata obecně štěkat neumí.

Nahlásit jako SPAM
IP: 194.228.13.–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
13. 1. 2016   #19
-
0
-

~~ přidávání metod do zděděných tříd absolutně nemá smysl

K čemu by mi byl potomek, který neumí nic navíc oproti předkovi? V takovém případě by (alespoň pro mne) dědičnost ztratila smysl.

hu

Nahlásit jako SPAM
IP: 195.178.67.–
KIIV
~ Moderátor
+43
God of flame
13. 1. 2016   #20
-
+1
-
Zajímavé
Kit +

#19 hlucheucho
Nekdy se to vyuzit da. Treba trida zvire muze mit ciste virtualni metodu vydavaZvuk(), kterou pak v jednotlivych specializacich implementujes ruzne.

Nahlásit jako SPAM
IP: 62.168.56.–
Program vždy dělá to co naprogramujete, ne to co chcete...
Kit+15
Guru
13. 1. 2016   #21
-
0
-

#19 hlucheucho
Viz mé vysvětlení. Nedá se v tom případě využít polymorfismu a aplikace je pak plná operátorů instanceof. Dědičnost je často nadužívána a Barbara Liskov dala pravidla pro situace, kdy je použití dědičnosti rozumné a kdy už ne.

U mého zvířete štěkání řeším následovně: 

Zvíře *zvíře = new Pes();zvíře->notify("Přišel cizinec");

Pes má v metodě notify() zapsáno, že má na cizince štěkat, Husa kejhat a Kočka zdrhnout.

Když pak dáš všechna zvířata do kolekce Dvorek, můžeš jim observerem rozeslat oznámení, co se stalo. Každé zvíře pak ví, co má dělat.

Nahlásit jako SPAM
IP: 194.228.13.–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
13. 1. 2016   #22
-
0
-

Detailista (lidově rejpal): vydavaZvuk() je možné jen u některých zvířat. Představ si třeba kapra.

hu

Nahlásit jako SPAM
IP: 195.178.67.–
Kit+15
Guru
13. 1. 2016   #23
-
0
-

#22 hlucheucho
To je jednoduché: U kapra naimplementuješ ticho nebo šplouchnutí, ale ta metoda vydavaZvuk() tam být musí - třeba prázdná.

Nahlásit jako SPAM
IP: 194.228.13.–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
KIIV
~ Moderátor
+43
God of flame
13. 1. 2016   #24
-
0
-

Rejpal^2: i ticho je zvuk :D

Nahlásit jako SPAM
IP: 62.168.56.–
Program vždy dělá to co naprogramujete, ne to co chcete...
13. 1. 2016   #25
-
0
-

#24 KIIV

Taky máš tinitus?

hu

Nahlásit jako SPAM
IP: 195.178.67.–
Kit+15
Guru
13. 1. 2016   #26
-
0
-

#25 hlucheucho
U strategií je obvyklé, že když klikneš na nějakou jednotku, tak se ti nějakým způsobem zahlásí. Bojový kapr by v takovém případě asi plácnul ploutví či ocasem, což jsou regulérní zvuky.

Nahlásit jako SPAM
IP: 194.228.13.–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
ondrej39+1
Věrný člen
13. 1. 2016   #27
-
+1
-
Zajímavé
Kit +

#19 hlucheucho
To nevadí, dědičnost (v případě, kdy rodičovská třída obsahuje i něco jiného než samé pure virtual metody a není tedy pouhým placeholderem pro interface) obecně je zbytečně přeceňovaná a špatně používaná.

Nahlásit jako SPAM
IP: 79.141.243.–
Inject all the dependencies!
PiranhaGreg0
Stálý člen
14. 1. 2016   #28
-
0
-

#17 ondrej39

Z čehož je logické, přidávání metod do zděděných tříd absolutně nemá smysl, protože nikdy nebudou v aplikaci dostupné.

Takže tím že je v C# všechno potomek třídy Object je vlastně celej C# špatně?  

Nahlásit jako SPAM
IP: 147.32.126.–
Kit+15
Guru
14. 1. 2016   #29
-
0
-

#28 PiranhaGreg
Myslel jsem si, že s tím někdo přijde, protože i mně to proběhlo hlavou.

Jde o to, že při správném používání dědičnosti současně využíváš polymorfismus: 

Rodič objekt = new Potomek(...);

Jako rodičovská třída má být taková, která je co nejblíž třídě Object, ale aby splňovala požadované rozhraní. Už mnohokrát jsem na levé straně použil třídu Object, protože jsem nic speciálnějšího nepotřeboval - stačilo mi překrýt některé standardní metody třídy Object a bylo to.

Stejně dobře může být na levé straně místo třídy nějaké rozhraní. Můžeš pak využívat nejen standardní metody třídy Object, ale i metody definované v tom rozhraní.

Proč to všechno? Abys mohl s takovými objekty pracovat jednotně. Je jedno, které třídy je potomkem, každý objekt má například metodu ToString(), která ti velmi dobře poslouží při prezentaci objektu. Klidně můžeš napsat příkaz 

Console.WriteLine(objekt);

a víš, že se ti nějakým způsobem vypíše. Pokud se ti výpis nelíbí, stačí jen napsat metodu ToString() do třídy, podle které byl vytvořen ten objekt tak, aby se ti výpis líbil.

Je toho na celý článek, co všechno se s tím dá dělat.

Nahlásit jako SPAM
IP: 2a00:1028:83a0:37a6:651c:...–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
PiranhaGreg0
Stálý člen
14. 1. 2016   #30
-
0
-

#29 Kit
No to jsi ale moc nereagoval na výrok

Z čehož je logické, přidávání metod do zděděných tříd absolutně nemá smysl, protože nikdy nebudou v aplikaci dostupné.

kterej mi prostě přijde nesmyslnej. Jinak principy OOP a k čemu je to dobrý znám moc dobře. Nedávno jsem dělal na projektu ve SmallTalku, tak bych mohl vyprávět  .

Nahlásit jako SPAM
IP: 147.32.126.–
Kit+15
Guru
14. 1. 2016   #31
-
0
-

#30 PiranhaGreg
Tak už to tolik nerozebírej a zkus to nějak rozdýchat. Nemusíme přece slovíčkařit.

Nahlásit jako SPAM
IP: 194.228.13.–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
ondrej39+1
Věrný člen
14. 1. 2016   #32
-
0
-

#28 PiranhaGreg
Pokud by cílem .NET frameworku bylo naprogramovat dle LSP, pak ano. Nicméně je vždy třeba myslet na to, že LSP a SOLID principy, do nichž LSP také spadá, které je stejně jako všechno možné brát jako pomůcku návrhu softwaru, ale nemusíš se jimi zcela striktně řídit. Jsou různé ideologie jak software tvořit.

Tak jako tak, LSP by se měl vztahovat převážně na objekty modelované tebou samým, nepředpokládám, že budeš vyvíjet samostatný .NET framework. Pokud se zabýváš vlastním vývojem a chceš ho dělat dle SOLID principů, pak je třeba brát v ohled i LSP.

Nahlásit jako SPAM
IP: 78.156.159.–
Inject all the dependencies!
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, 39 hostů

Podobná vlákna

Dědičnost — založil cheeester

Dedičnosť — založil Grungy

Dedičnosť — založil zonda

Dědičnost — založil paul27

Dedicnost ... — založil Koudis

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ý