Vlákno názorů k článku Rychle, rychleji až úplně nejrychleji s jazykem Go od ondra.novacisko.cz - Vše co má nový jazyk zvládat, tak zvládne...

  • Článek je starý, nové názory již nelze přidávat.
  • 15. 12. 2009 12:40

    ondra.novacisko.cz (neregistrovaný)

    Vše co má nový jazyk zvládat, tak zvládne C++ pomocí vhodných knihoven stejně snadno. Nač vymýšlet novou syntaxi?

  • 15. 12. 2009 14:00

    Ped7g

    Budes prohlasen kacirem, nebo ludditem nebo co…
    Ale pridam se, taky verim v C++, hlavne pokud je rozumne pouzivane.

  • 16. 12. 2009 11:58

    Ped7g

    K cemu to je DOBRE?

    Podle mne je lepsi rozumne pracovat se stackem a lokalnimi promennymi ve spojeni s konstruktory a destruktory. Tech par zbylych vyjimek kde je rozumnejsi pouzit heap nez stack v 95% pripadu osetri kontejner, takze zase explicitni new/delete ani GC neni potreba.

    GC podle mne zpusobuje ze prumerny programator ani nevi kde pouziva RAM a ani nadprumerny programator nevi kdy se tam RAM opravdu uvolni. Ja mam rad plnou kontrolu nad obojim. Jsem ochotny doznat ze nadprumerny programator bude u vyvoje bezne aplikace™ s GC produktivnejsi, ale ne o moc. Prumerny lepic kodu IMHO produktivnejsi nebude, nebude mit sice prime memory leaks, ale vykon aplikace a jeho porozumeni tomu co pise bude jeste nizsi nez v C++, takze to co usetri na sprave pameti ztrati na lazeni a udrzbe.

    Vid. http://www.ultimatepp.org/www$uppweb$overvi­ew$en-us.html a druhy odsek „Everything belongs somewhere“. Pro mne osobne to 100% funguje.

  • 16. 12. 2009 12:17

    Michal Kára (neregistrovaný)

    Spis by bylo presnejsi psat „funguje to v aplikacich, ktere pisu ja“. Pokud mate v pameti slozitejsi struktury, ktere na sebe krizove odkazuji, objekty se ruzne vytvari a rusi, tak se stejne nakonec bez nejakeho reference countingu neobejdete. A od nej je jen kousek ke GC :-)

    Samozrejme jsou naopak aplikace, kde je _opravdu_ potreba mit pamet co nejvic pod kontrolou a na ty se GC zase nehodi.

  • 16. 12. 2009 12:36

    ondra.novacisko.cz (neregistrovaný)

    Nepochopení zástánců GC je v tom, že každý objekt musí někomu patřit. Právě že v OOP struktuře je GC něco jako „Bůh“ a my ateisti na Boha nevěříme. Komu patří objekt, který už nikdo nehce? Bohu? Ten ať rozhodne o jeho osudu.

    Jenže ateista ví, že žádný Bůh není a tak už primárně navrhuje strukturu tak, aby vlastník byl znám. Ten pak má na starost úklid objektů, jež již nejsou potřeba. Přitom ono „není potřeba“ nemusí být záležitost jen o analýze vzájemných odkazů. Může to být rozhodnutí dané okamžikem. Uživatel klikne na „Close Document“ a v ten okamžik vlastník objektů „dokument“ provede hromadný úklid, bez ohledu na to, kolik bylo v dokumentu kruhových referencí.

    Ano, chápu, existují možnosti, že někdo úklidem odstraní objekty, které odkazuje někdo z vnějšku. Opět je to ale chyba struktury, protože tento pirátský odkaz by měl být oznámen vlastníkovi, ale nebyl a to je špatně. Existuje tu nějaká hierarchie, něco, co říká za co kdo zodpovídá. A proto většinou člověk GC nepotřebuje, protože program navrhuje už s ohledem na existenci hierarchie a tedy přirozených vlastníků a správců, což jak bylo správně uvedeno, jsou kontejnery, a jejich správci.

    A kdyby náhodou to „nešlo nijak udělat“ stále si lze v 95% procentech pomoci čítačem referenci, a pokud je dobře udělanej, tak uživatel vůbec nepozná, že to tak funguje a pracuje s objekty jako v Javě.

    A kdyby se náhodou někdo dostal k těm 5% procentům, tak o pár příspěvků níže jsem postnul 2 odkazy na C++ řešení GC, ale jak sami zjistíte, jedná se zase jen o nástroj, který pomáhá vlastníkovi … pastýři sehnat dohromady své ovečky a zajistit mezi nimi vztahy a zbavit se nepotřebných… Tedy je to jakási forma kontejneru obsahující nástroj na procházení zpětných odkazů pomocí technik hledání kostry grafu. Nic moc lowlevel, ale funguje to jako GC… ale zatím jsem to moc neupotřebil.

  • 16. 12. 2009 14:23

    Michal Kára (neregistrovaný)

    To by mne zajimalo, jak jste dosel k tomu, ze zastanci GC nechapou koncept vlastnika. Aspon ja to chapu velmi dobre. Ale pokud nemate ciste stromovou strukturu, tak musite mit mechanismus, jak vlastnikovi sdelit, ze jeho objekt odkazujete a naopak, ze jste odkaz zrusil.

    No a problem nastava v okamziku, kdy nekdo tu (od)notifikaci napsat zapomene. Takova chyba se typicky projevuje nekdy uplne jindy a uplne jinde, nez kde k ni opravdu doslo a ladit to je peklo (a to casto i kdyz pouzijete nastroje pro toto urcene).

    Pouziti GC setri spooooustu casu – jednak na psani kodu slouzici pro tu infrastrukturu vlastniku (pocitani odkazu, notifikace) a jednak na ladeni memory leaku. Samozrejme do realtime systemu bych GC nedaval (nebo aspon ne ten Javovy), ale to je zas jina kapitola :-)

  • 16. 12. 2009 14:58

    ondra.novacisko.cz (neregistrovaný)

    „No a problem nastava v okamziku, kdy nekdo tu (od)notifikaci napsat zapomene.“

    Sám víte, že tohle do OOP nepatří. K čemu jsou setři a getři. Aby vlastník (objekt) věděl, že mu někdo pod rukou mění stav nějakého jeho memberu. A tady máte analogii. Pokud mám správce něčeho … nějakého mraku objektů… nemohu s nimi pracovat jak chci… Musím neprve za správcem, aby mi dal oprávnění… v řeči programátora obdržím objekt, se kterým pak mohu do mraku vstupovat. Vstupenka je pro cizáka, a zároveň notifikace pro vlastníka, protože si eviduje vydané vstupenky.

    Pokud už se dva správci domluví na zvláštnim vztahu… sdílení… pak nastupují počítané reference, se kterými často vystačíte…

    „Pouziti GC setri spooooustu casu – jednak na psani kodu slouzici pro tu infrastrukturu vlastniku (pocitani odkazu, notifikace) a jednak na ladeni memory leaku“

    Ano… možná bych GC uvítal v DEBUG režimu… ale oni naštěstí existují nastroje, které to umí bez GC :-) Třeba valgrind, nebo Visual Studio má nástroj na detekci memory leaku. Pokud ale budete pracovat čistě a nedělat prasárny, tak jej kolikrát ani nevyužijete. Naopak já jako programátor C++ mám s Javovským GC obrovské problémy, když adaptuju postupy v programování, kde hodně využívám sílu destruktorů, a kde nejsem schopen v Javě takový destruktor vynutit ani ručně (a když už to nějak obejdu, tak Javu degraduju na procedurální jazyk).

  • 16. 12. 2009 15:52

    Michal Kára (neregistrovaný)

    Zapomnel jste na tu odnotifikaci, tu zadnym standardnim OOP mechanismem automaticky neudelate. Navic delat odnotifikaci po kazdem getu objektu je… ehm… dost nepohodlne. Pokud mate single-thread aplikaci, muzete to obejit dvema variantami getteru – jednou s notifikaci a druhou bez, ale zase kdyz omylem pouzijete nespravnou…

    Ano, nastroje jsou, taky jsem je pouzival… ale kdyz nad objektem provadite za dobu jeho zivota (nekolik hodin / dni) treba 1000 referenci / dereferenci a jednou zapomenete odreferencovat, tak tezko zjistite, ktera. A ne vsechny provozni problemy se daji vyvolat v kontrolovanem prostredi.

    To neni jenom o cistote, ale taky o povaze problemu. Nektere problemy vyzaduji jednoduche pametove struktury, nektere slozite.

    To by mne zajimalo co to je ta „sila destruktoru“ :-D Pokud na tom trvate, pseudo-destruktor je mozne si udelat i v Jave. Bude fungovat stejne jako v C++, akorat nebude realne uvolnovat pamet objektu.

  • 16. 12. 2009 16:22

    ondra.novacisko.cz (neregistrovaný)

    " ale kdyz nad objektem provadite za dobu jeho zivota (nekolik hodin / dni) treba 1000 referenci / dereferenci a jednou zapomenete odreferencovat, tak tezko zjistite, ktera. A ne vsechny provozni problemy se daji vyvolat v kontrolovanem prostredi."

    Mluvíte o C++? Znáte chytré ukazatele? Věděl byste, že tenhle případ vám s chytrým ukazatelem nenastane.

    „To by mne zajimalo co to je ta „sila destruktoru“ :-D “

    Takže nemluvíte o C++ ale o C. To byste se tak hloupě neptal. Síla v destruktoru je ta, že se destruktor provede v okamžik, kdy platnost objektu končí. Ne „až někdy“. Destruktoru právě používají třeba ty chytré pointery. Ale co třeba uzavírání resourců? A abych šel dál, mám třeba resource, třeba jako „externí soubor“. Funguje tak, že se normálně do něj zapisujete a při destrukci se uploaduje na server. Uživatel (programátor – uživatel knihovny) nepozná, že se jedná o „externí soubor“, pracuje s ním stejně, jako by byl lokální.

    „Pokud na tom trvate, pseudo-destruktor je mozne si udelat i v Jave. Bude fungovat stejne jako v C++, akorat nebude realne uvolnovat pamet objektu.“

    Nebude! Musíte je volat ručně! Já jsem se o něco takového pokoušel tady: http://bredy.novacisko.cz/?… … mno ale nic moc.

    Destruktory jsou navíc pomalé, volají se pozdě. Viz článek.

  • 16. 12. 2009 17:49

    Michal Kára (neregistrovaný)

    Nojo, ja v C++ delal velmi malo, takze mi auto pointery unikly. Sikovna featurka, pravda. Tim se da pocitani referenci z velke casti automatizovat – akorat s cykly to ma trochu problem (mozna nekdo udelal auto ptr, ktery tohle resi(?)).

    Ano, „destruktor“ se musi volat rucne, s auto ptr vam to C++ zavola samo, to je pravda. Na druhou stranu, jediny pripad co mne napada kdy na tom volani fakt zalezi je prave uvolnovani resources, to je v Jave / .C# to je trochu neelegantni (nutnost obalovat try – finally), ale nastesti ne prilis caste.

  • 17. 12. 2009 9:07

    Michal Kára (neregistrovaný)

    Takže abych to celé shrnul, není to o GC, ale o tom, že Java nemá mechanismus jak dát objektu vědět, že vypadnul z kontextu. Ten by principielně asi nebyl problém udělat, ale v Javě není a její autoři pro to asi mají dobré důvody. A jeden takový mně napadl:

    Co když vypadává z kontextu více objektů a při implicitním volání destruktoru jednoho z nich dojde k vyjímce? Co dělat? Jsou tři možnosti:

    1) Vyhodit vyjímku nahoru. To je špatně, protože nebudou zavolány destruktory dalších objektů a kód pro obsluhu vyjímky s tím nemůže nic udělat, protože ty objekty už nemá v kontextu.
    2) Vyjímku ignorovat a volat destruktory dalších objektů. To je špatně, protože se nikdo o vyjímce nedozví.
    3) Vyjímku „pozdržet“, zavolat destruktory dalších objektů a pak ji teprve poslat nahoru. Ale co když další destruktor vyhodí další vyjímku? Tak musíme jednu z nich skrýt a to je špatně.
    4) Dá se představit řešení kdy by se vyjímka pozdržela, a pokud je vyjímek více, vytvořila by se nová „zapouzdřující“ vyjímka… ale to by bylo dost hnusné a krkolomné řešení.

    Takže jak je vidět, to volání destruktoru na objekty vypadávající z kontextu je pěkné, ale má zásadní problém s handlováním vyjímek. BTW, jak tohle řeší C++?

  • 17. 12. 2009 18:09

    ondra.novacisko.cz (neregistrovaný)

    Destruktor může hodit výjimku. Ačkoliv někteří programátoři důsledně odmítají výjimky v destruktorech a důsledně deklaruji destruktor s klíčovým slovem throw() — bez výjimek.

    Nicméně, pokud destruktor vyhodí výjimku, přesto probíjá dál volání destruktorů předků a memberů. Destruktor, který vyhodil výjimku je považován za provedený.

    V případě, že další problém nevznikne, výjimka je vyhozena až do nějakého catch handleru a ten jí zpracuje. Destrukce většinou probíhá hned, ne „až někdy“ u chytrého pointeru v destrukci toho pointeru. Takže příjemce výjimky je znám.

    Pokud dojde k dvojité výjimce, tedy při zpracování destruktorů v důsledku výjimky, a tato výjimka není odchycena v tomtéž desruktoru v jehož těle (nebo v nějaké volané funkci) vznikla, program skončí na terminate(). Tohle je pravidlo. Protože prostě teď by letěly dvě výjimky a nebo by si musel program vybrat. Což je nepřípustné. Nicméně programátor má možnost tohle postihnout. Má k dispozici funkci std::uncaught_ex­ception() která vrací 1, pokud se provádí unwind z výjimky. V tom případě je program varován a může se zachovat podle toho.

    Já to řeším tak, že prochází-li výjimka destruktorem, je to jako signal „rollback“, Pakliže s uzavřením resourců je spojena nějaká činnost, třeba upload dat, která může skončit výjimkou, tak se vynechá, protože rollback. Pokud nemohu takové činnosti zabránit, tak výjimku odchytnu a zahodím. Už jedna výjimka je in progress, další výjimka může být zavlečená, tedy vznikla v důsledku té první výjimky.

    Tohle ještě rozšiřuji tak, že když letí výjimka z mé knihovny a má vyletět výjimka také patřící do mé knihovny, výjimka si zjistí, že jedna už letí a protože každá výjimka z mé knihovny má tzv. „reason“ (stejně jako v javě, tedy odkaz na další výjimku, která taky může mít reason), pak se tato výjimka přidá jako reason. Pokud už tam reason je, projde se řetěz a přidá se na konec řetězu.

  • 17. 12. 2009 18:20

    ondra.novacisko.cz (neregistrovaný)

    „Takže abych to celé shrnul, není to o GC, ale o tom, že Java nemá mechanismus jak dát objektu vědět, že vypadnul z kontextu. Ten by principielně asi nebyl problém udělat, ale v Javě není a její autoři pro to asi mají dobré důvody. A jeden takový mně napadl:“

    Ale jsou, finalizery. Ale jsou _P_O_M_A_L_E_ . Neprovádí se ihned, ale až si VM vzpomene. A běžně se stává, že si nevzpomene vůbec, nebo uvázne dřív, než si stačí vzpomenout. Zkuste v javě otevřít a zamknout soubor a ve finalizeru jej zamknout. Zkuste to teď udělat dvakrát. S tím, že mezitím vždy objekt opustíte, Jednou to projde, a podruhé skončíte deadlocku. Nenašel jsem žádný způsob, jak vyvolat finalizaci explicitně, aby se provedl předtím, než se pokusím soubor otevřít a zamknout podruhé.

  • 18. 12. 2009 11:34

    Michal Kára (neregistrovaný)

    Nejsou pomale, ale jsou neco jineho nez chcete – jsou volany pri GC a ne pri vypadnuti objektu z kontextu.

    Ad reseni s vice vyjimkami: Aha, tak to je dalsi spatne reseni, ktere mne nenapadlo :-D Samozrejme destruktory vetsinou vyjimky nevraci (explicitni), ale mohou kdykoli vyhodit runtime vyjimku.

  • 18. 12. 2009 15:16

    ondra.novacisko.cz (neregistrovaný)

    „Nejsou pomale, ale jsou neco jineho nez chcete – jsou volany pri GC a ne pri vypadnuti objektu z kontext“

    Ano, tím označuji, že jsou pomalé, tedy že se nevolají hned, ale až někdy. Chybějící „pravé destruktory“, nikoliv finalizery právě způsobují, že javu moc nemusím :-D. Hlídání memory leaku tak člověk zaplatí něčím jiným. Možná, kdyby Java uměla explicitní destrukci objektu, nebo objekty, které nelze sdílet a jsou likvidovány při opuštění kontextu.

    „Ad reseni s vice vyjimkami: Aha, tak to je dalsi spatne reseni, ktere mne nenapadlo :-D“

    A které je správné? Zjistíte, že žádné. I to javovské je špatné.

    „Samozrejme destruktory vetsinou vyjimky nevraci (explicitni)“

    Výjimky se nevrací, výjimky se hážou. Výjimka má dvoje poslání. Přerušit probíhající operaci a informovat volajícího. U vyjimky v destruktou není co přerušovat, takže její význam je spíš informační.

    C++0× bude umět řešit i tyhle typy výjímek,

  • 19. 12. 2009 17:20

    M. Prýmek

    Panove, nechci se Vam do toho zajímavého rozhovoru vměšovat, tak jen moje dva centy:

    Jazyk, který 1) nemá defaultně všechny metody virtuální* a 2) kde v konstruktoru se nedoporučuje volat virtuální metody**, nějak nemůžu považovat za rozumně objektový, i kdybych chtěl sebevíc…

    * nikdy si nejsem jistý, jestli opravdu tu kterou metodu nebudu nikdy chtít překrýt – asi proto, že nejsem žádný programátorský guru – nicméně to považuju za fakt divnou „feature“ jazyka… (bohužel se týká – zřejmě kvůli CLI, nevím – i jinak krásného boo :( )

    ** http://www.artima.com/…vercall.html

  • 19. 12. 2009 21:25

    ondra.novacisko.cz (neregistrovaný)

    1) C++ je kompromis mezi výkonem a čistotou. Valná většina metod nemusí být virtuální a tak je zbytečné věnovat výkon procesoru na virtualizaci metod. Prostě to něco stojí. Všichni nadáváme na pomalé počítače a sami si je ještě zpomalujeme

    2) No, ono to tak není. Můžete volat virtuální metody, ale zavolají se jen na té úrovni, která je zkonstruována. Konstrukce objektu není jednorázová záležitost a tak volání virtuálních metody nezpůsobí zavolání její reimplementace v potomkovi, protože v době konstrukce ten potomek ještě není zkonstruován. Totéž platí pro destruktor. Virtuální metoda v destruktoru se nezavolá do již zdestruované části objektu.

    Nadále odmítam diskutovat s lidma, který programování viděli z rychlíku a nazývají se programátory!

  • 20. 12. 2009 11:09

    M. Prýmek

    >> Nadále odmítam diskutovat s lidma, který programování viděli z rychlíku a nazývají se programátory!

    Zdůraznil jsem, že nejsem žádný programátorský guru. Programováním se neživím, takže je klidně možné, že mi něco uniká. „Programátorem“ jsem se nenazval, nebo ano?

    >> C++ je kompromis mezi výkonem a čistotou.

    S tím naprosto souhlasím a je to v duchu toho, co jsem psal. Ve skutečných objektových jazycích (z nejrozšíře­nějších třeba Objective C) si IMHO (!) objekty POSÍLAJÍ ZPRÁVY a samy se rozhodují, jak na ně bude vhodné zareagovat. V „klasických“ jazycích se volají funkce.

    No a pak je tu C++, které se tváří, že je objektové, a staré dobré volání funkcí ohýbá tak, aby VZDÁLENĚ PŘIPOMÍNALO posílání zpráv v OOP.

    Teď s dovolením zopakuju, co jsem napsal: Jazyk, který […] nějak nemůžu považovat za rozumně objektový, i kdybych chtěl sebevíc…

    Čili jestli je v C++ zprasená dědičnost nebo ne (což byl Váš spor s kolegou), mi nepřijde až tak důležité v situaci, kdy celý jazyk je KOMPROMISEM – funkcionálním jazykem, který se tváří objektově. Ano, pro spoustu aplikací je to velmi vhodný kompromis. Netvařme se ale, že C++ je krásný objektový jazyk. Není.

    >> No, ono to tak není. Můžete volat virtuální metody, ale zavolají se jen na té úrovni, která je zkonstruována.

    No a to je právě ono. Objekty v C++ nejsou objekty. Jsou to v principu pouhé tabulky callbacků a programátor musí brát ohled na to, jak tyhle tabulky callbacků překladač (!!!) konstruuje. Ok, chápu, že to mnoha lidem vyhovuje a nezazlívám jim to, jen prostě takový jazyk nemůžu považovat za objektový, stejně jako nepovažuju za OOP ohýbání čistého C tak, aby připomínalo OOP.

    P.S. nijak na Vás naútočím, ani nechci šířit flame – na tom, co píšu, se myslím shodujeme – C++ je kompromisem mezi OOP a funkcionálním programováním. Některým z nás tento kompromis vyhovuje, jiným ne. Pro všechny ale platí, že bychom se neměli tvářit, že C++ je opravdový OO jazyk.

  • 20. 12. 2009 12:06

    ondra.novacisko.cz (neregistrovaný)

    „POSÍLAJÍ ZPRÁVY“

    S tím nemám problém

    „aby VZDÁLENĚ PŘIPOMÍNALO posílání zpráv v OOP.“

    Ano. přesněji, aby C++ bylo co nejvíc OOP a zároveň využilo co nejvíc výkonu procesoru. Od toho bodu na jednu stranu jsou jazyky co nejsou tak OOP a jsou rozhodně rychlejší než C++. Na druhou stranu jsou jazyky bližší OOP avšak jakákoliv další abstrakce stojí mnohonásobně výkonu. Já se domnívám, že to je příliš drahá cena. V C++ zvládnu spoustu OOP technik bez toho, aniž bych platil výkonem. A je v dannou chvíli jedno, jestli si objekty posílají zprávy, nebo volají metody. Nejzajímavější je to, že když si do C++ dopíšu určitý framework, udělám tam i objekty, které si opravdu budou posílat zprávy a nebudu potřebovat tříd a interfaců. Zatímco v C++ naprogramuju i čistý OOP framework, obávám se že v čistém OOP frameworku těžko naprogramuju C++.

    „No a to je právě ono. Objekty v C++ nejsou objekty“

    Ne, tady jste úplně vedle. Pokud se v OOP řeší dědičnost a to, že objekt se skládá z předka a je rozšířen o potomka, pak konstrukce takového objektu je prováděna nejprve konstrukcí předka a následně potomka. Předek ale nemůže volat potomka, když ten neexistuje. Tady jste se bohužel spletl, protože technicky opravdu není problém, aby konstruktor zavolal virtuální metodu potomka. Ale záměrně to neudělá, protože to je špatně. Je to chyba, a právě proto jsem byl naštván, protože to ukazuje hlubokou neznalost OOP. Věřte mi, že tohle záměrné omezení stojí při konstrukci objektu nějaký výkon, protože každý konstruktor musí reinicializovat vtabulku a konečný potomek ji pak ještě znova nechá přepsat podle sebe. Přitom by mohl vtabulku nastavit jednou před zahájením konstrukcí předků a bylo by to rychlejší a vyhovalo by to vašim požadavkům. Jenže by to bylo nepřijatelné chování.

    Nadávám proto, že mnoho lidí si tu představuje programování jako znalci sportu, kteří veškerý sport znají ze sportovního kanálu. Všichni víme jak je C++ objektově zmršený jazyk a přitom nevíme nic o objektovém programování. Nakonec mě dorazí prohlášení, že OOP jazyk musí obsahovat garbage collector, což je přesně to, proč Javu ani C# nepovažuju o nic objektovější, než C++ a díky tomu, že člověk platí výkonem, tak jsou ještě horší. (GC se stará o paměť na úkor ostatním resourců, které je třeba spravovat ručně, tery neobjektově)

    A vy jste přesně ten typ člověka, který ví, jak je to špatné, aniž by věděl, jak je to vlastně správně.

  • 20. 12. 2009 12:29

    M. Prýmek

    >> Na druhou stranu jsou jazyky bližší OOP avšak jakákoliv další abstrakce stojí mnohonásobně výkonu.

    Skutečně máte představu o tom, jakou režii vyžaduje skutečné zasílání zpráv v Objective C oproti volání callbacků v C++? Skutečně víte, že je to „mnohonásobně“? Já to nevím, ale nezdá se mi, že by programy na MacOS běhaly nejak podezřele pomalu…

    >> . V C++ zvládnu spoustu OOP technik bez toho, aniž bych platil výkonem.

    V C++ zvládnete pár technik, které OOP vzdáleně připomínají. Viz předchozí příklad s „zvys se o jeden stupen“

    >> Ne, tady jste úplně vedle. Pokud se v OOP řeší dědičnost a to, že objekt se skládá z předka a je rozšířen o potomka, pak konstrukce takového objektu je prováděna nejprve konstrukcí předka a následně potomka.

    Když už nadáváte na lidi, kteří nechápou OOP, tak si alespoň uvědomte, jak teď demonstrujete to, co jsem psal: C++ naučilo lidi COSI považovat za OOP.

    V tomhle konkrétním případě Vás C++ naučilo považovat objekt za „skládající se“ (!!!) z předka a potomka.

    Skutečně máte pocit, že „Kočka se skládá z kočky a savce“?

    Ne, správně je: kočka umí vše, co umí savec – a ještě něco navíc. TOTO JE OOP!

    To, co popisujete, by bylo maximálně skládání objektů – jenže to se v C++ moc nenosí – právě proto, že C++ neumí rozumně delegovat zprávy. Maximálně to obejít trapárnou typu zvysSe() { vlozeny objekt.zvysSe(); }
    …a to je opravdu hodně odlišné od skutečně objektového „všechny zprávy začínající na "vtip“ přepošli vloženému objektu „skladVtipu“ ".

    >> Předek ale nemůže volat potomka, když ten neexistuje.

    Opět demonstrace zhoubných návyků z C++. Žádný předek nevolá žádného potomka. Objekt sám sobě např. posílá zprávu „tvař se“ – a kočka se prostě od narození až do smrti tváří jako kočka, nikoli chvilku jako savec a poté jako kočka.

    =====

    Jde mi o jediné – pravé OOP je vysoce intuitivní a člověk nemusí nad ničím moc přemýšlet. RádobyOOP v C++ je spíš ve stylu „Musím dodržovat tisíc pravidel, pokud chci, aby to vypadalo objektově“.

  • 20. 12. 2009 12:12

    M. Prýmek

    Ještě dodám jednu drobnost: C++ IMHO svým rozšířením způsobilo, že lidi jako OOP vnímají určité programátorské postupy, které vlastně vůbec objektové nejsou. Jednoduchý příklad:

    Mám metodu zvys(obj), která má způsobit, že předaný objekt bude „zvýšen o jeden stupeň“. Je jí jedno, jestli je to číslo (zvyš o jedničku) nebo barák (zvyš o jedno patro).

    Podle mého názoru (!) je „objektový postup“ takový, že dostanu nějaký objekt, o kterém nic nevím, a řeknu mu „zvyš se o jeden stupeň“. On sám musí vědět, co to pro něj konkrétně znamená, mě to nezajímá.

    Objekt se buď umí „zvýšit o jeden stupeň“, nebo umí tuhle chybějící schopnost něčím nahradit (např. pošle adminovi email „Jsem objekt "Kočka číslo 254“, měl jsem provést operaci zvysSeOJedenStupen, ale nevím, jak na to – něco s tím udělej"), popř. když tuhle schopnost (reagovat na neznámé zprávy) nemá, vyhodí nějakou výjimku.

    Pokud metoda zvys(obj) chce mít jistotu, že objekt požadovanou schopnost skutečně má, má se ho zeptat „umíš se zvýšit o jeden stupeň?“ a podle odpovědi se zařídit.

    „Poloobjektové“ C++ zavedlo zvyk „posílat“ jen ty zprávy, které objekt zaručeně umí zpracovat – a skoro všichni to považujou za úplně normální rys OOP. Jenže to není normální, to je jenom úlitba tomu, že tabulka callbacků si hraje na objekt.

    Mechanismus interfaců je sice ve většině případů použitelný k plné spokojenosti, ale opět není čistočistě objektový – vychází IMHO (!) právě z popsané „úchylky“, kterou za normální protlačilo C++.

    S „virtuálníma metodama“ je to IMHO podobně – ve skutečném OOP takový koncept vůbec nemá co dělat, protože je samozřejmé že objekt Kočka je objektem Kočka po celou dobu své existnce – a že po celou tuhle dobu reaguje jako kočka a ne „občas jako savec“, protože teď zrovna ješte není kočkou, ale teprve savcem…

    Jaké to má kořeny, by museli říct povolanější, já osobně mám podezření, že to je tím, že C++ explicitně neodlišuje zprávu (dle Objective C) „alloc“ (alokace místa v paměti), která se posílá třídě, a „init“, která se posílá už objektu.

  • 20. 12. 2009 12:49

    M. Prýmek

    Nicméně tahle debata asi nemá většího smyslu, jestli se Vám v C++ programuje dobře a dobře ho znáte, používejte ho dál k plné spokojenosti!

    Jenom abych to uzavřel, krátký příklad:

    Mám objekt třídy X, který má několik předků, z nichž někteří jsou definováni v knihovně, k jejichž zdrojákům nemám přístup nebo je nemůžu měnit. Objekt má spoustu metod, pro jednoduchost žádná z nich nevrací návratovou hodnotu.

    Jednoho dne zjistím, že bych tenhle objekt chtěl umístit radši na výkonný server, ale zároveň s ním pracovat ve všech ohledech pořád stejně jakoby byl lokální. (to není nijak sci-fi scénář)

    Programuju-li ve (skutečném) OOP paradigmatu, vytvořím proxy objekt s jedinou metodou, která všechny zprávy posílá objektu, který žije na serveru.

    Co udělám v „objektovém“ C++? Řeknu šéfovi, že na přesun objektu na server se mělo myslet při návrhu už od začátku a teď už s tím nejde (čistým způsobem) nic dělat.

    Čili chcete-li, můžete v kontextu C++ mluvit o dědičnosti, polymorfismu, skládání atd. a típádem i OOP, jen to prosím nevyžadujte po všech ostatních :)))

    Přeji hezkou neděli a dávám vám poslední slovo :)

  • 20. 12. 2009 16:18

    ondra.novacisko.cz (neregistrovaný)

    Nečetl jsem to celé, nebudu, nemám na to čas. Kočka se skládá ze savce a kočky. Nevím, proč se mne snažíte přesvědčit o opaku. Nemůžete naučit savce mňoukat, dokud se nevyvine v kočku.

    Bavíte se o tom, co je objekt, a zapoměl jste na základní věc. Objekt musí vzniknout. OOP teorie vznik objektu nějak extra neřeší, zatímco C++ je praktické řešení této teorie. Zkuste v jiném objektovém jazyce donutit kočku mňoukat v době, kdy se snažíte vytvořit společné znaky všem savcům. Uvidíte že pohoříte.

    Tečka.

  • 20. 12. 2009 16:28

    ondra.novacisko.cz (neregistrovaný)

    Proxy objekt do OOP nepatří.

    Proxy objekt samozřejmě můžete udělat i v C++. Stačí jen proxovat rozhraní, kterým bude objekt komunikovat. v C++0× se plánuje přidat pro tyhle případy zvláštní konstrukce, které proxy objekty budou tvořit podle vzoru automaticky.

    Komunikace pomocí zpráv a pomocí interface se liší jen v jednom detailu. Zatímco objektu můžete poslat libovolnou zprávu, tak při použití interfacu pouze určujete jakýsi „komunikační protokol“, což je běžná věc v jakékoliv jiné komunikaci. Množství protokolů v C++ přitom není omezen a teoreticky můžete pro každou zprávu generovat samostatný protokol (interface). Objektu lze zaslat zprávu podle libovolného protokolu s tím, že pokud objekt protokol nepodporuje, tak komunikovat nebude. Stejně jako v OOP. Interface pouze jen z výhodou združuje zprávy patřící ke stejnému protokolu, čímž se samozřejmě šetří výkon, protože ověřování protokolu (hledání interface) samozřejmě opět stojí nějaký výkon navíc.

    Stejně jako poslání zprávy… musí tu zprávu vyhledat v nějaké tabulce, rozparsovat parametry, případně identifikovat objekt zprávy… všechno stojí výkon.

    C++ v zásadě umí vše, co by měl umět OOP jazyk. Jen někdy se člověk snaží ne úplně všechno použít, protože balancuje mezi výkonem a užitečností.

    Naučte se C++.

  • 20. 12. 2009 20:31

    M. Prýmek

    Řekl jsem, že Vám nechám poslední slovo, tak to nebudu porušovat.

    C++ se učit nebudu, omlouvám se, skončil jsem s ním tak před deseti lety :)

    Jestli máte chuť se trochu zamyslet, co je vlastně OOP a jestli kočka někdy ve svém životě byla pouhým savcem bez kočky :), tak viz např.:

    http://www.mujmac.cz/…cocoa_9.html (co umí skutečný objekt – doporučuji se zamyslet nad respondsToSelector, conformsToProcotol, performSelector)

    http://www.mujmac.cz/…ocoa_12.html (Kategorie – úžasná věc, kterou neznám v jiném jazyce)

    http://www.mujmac.cz/…ocoa_13.html (skutečně OO skládání objektů)

    http://www.mujmac.cz/…ocoa_34.html (skutečný proxy objekt)

    Pokud zájem nemáte, přeji mnoho spokojených a šťastných chvil s C++ :)

    Howgh.

  • 18. 12. 2009 23:11

    Pavel Lang

    V javě stejně jako v C# se destruktor NEMUSÍ zavolat. Navíc tuším, že pokud se zavolá, neznamená to automaticky, že se zavolá ve stejném vlákně.
    V C# je interface IDisposable (definuje metodu Dispose()), kterému, pokud je to ve stejném bloku, pěkně pomáhá keyword „using“. Pak lze použít ještě samostatnou metodu např. s jménem Close(), Destroy()… Dosti výmluvně na to, aby se na to nezapomnělo a projistotu ještě dát do komentáře u konstruktoru „návod k použití“.

  • 21. 12. 2009 9:51

    Ped7g

    To jako ze to ma clovek rucne explicitne volat? To mi zni takhle po ranu jako opravdu spatny vtip. Ja jsem rad kdyz objekt nezapomenu vytvorit, natoz se jeste starat o to jak zanikne. (kde zanikne definuji tim kde ho vytvorim, takze to neni takovy problem, ale detaily o zaniku rad prenecham C++, s tim ja uz nechci nic mit)

  • 21. 12. 2009 11:29

    Pavel Lang

    Tak teď tomu co píšete opravdu nerozumím. Není řeč o zániku objektu, ale o zavolání destruktoru, tedy metoda ~JmenoTridy(). V C++ se jazyk postará o zánik pouze, pokud ho vytvoříte na stacku. Pokud s objektem potřebujete pracovat mimo, je zde krásné klíčové slovo new. Ke každému new v C++ automaticky patří delete. V Javě či managed jazycích a v jazycích, kde vládne garbage collector se objekt neruší explicitně, ale zanikne až když není potřeba. V případě použití objektu lokálně objekt zaniká opuštěním dané scope (respektive je označen pro likvidaci). Jinak zafunguje počítání referencí a jiné vychytávky.

    V C++ se o rušení objektů starat musíte.

    Naproti tomu destruktor je metoda, která se v C++ volá automaticky se zrušením objektu (buď opuštěním scope nebo pomocí delete). Destruktor je takto volán synchronně. Oproti tomu se v managed jazycích objekty neruší hned, ale až když je zavolán GC. Z toho vyplívá, že se destruktor nemusí zavolat hned a nebo nikdy – například pokud je program ukončen, pamět se uvolní celá. Navíc destruktor nebeží ani ve stejném vlákně. Pro objekty, které potřebují v C# likvidaci nějakých naalokovaných zdrojů (různá handle, soubory, okna) existuje IDisposable interface a metoda Dispose(), která pokud je objekt vytvořen v using kontextu je volána synchronně a není nutno ji volat explixitně (i když je to samozřejmě axplicitní volání zabalené pomocí syntax. cukru)

  • 21. 12. 2009 12:43

    Ped7g

    „buď opuštěním scope nebo pomocí delete“

    No prave, proste objekt vlozim do takoveho scope, kde vim ze ma existovat a o desktruci ve vhodne chvili je tim padem vystarano, bez ohledu na to jakym zpusobem ze scope vylitnu.
    „new“ / „delete“ jsou nebezpecne, vetsinou se daji obejit pouzitim kontejneru na stacku (ktery si to hlida sam), nebo kdyz uz to nutne musi byt, tak auto_ptr, nebo wrapper objekt ktery si sam dela delete v destruktoru a ten instancovat na stacku. Ale v 95% pripadu new/delete neni vubec potreba, skoro vsechno resi bud lokalni instance v danem scope kde je objekt potreba, nebo kontejner pokud je toho vice.

    Podle mne klasicky new/delete a explicitni destrukci v C++ pisou uz jen masochisti. Zbytek bud presel na neco co ma GC, nebo to pise rozumne aby se s tim nemusel trapit.

  • 16. 12. 2009 12:24

    ondra.novacisko.cz (neregistrovaný)

    Jeden experimentální postavený čistě v C++ (žádné moc lowlevel věci, spiš nějaké šablony)

    http://bredy.novacisko.cz/?…

    http://bredy.novacisko.cz/?…

  • 16. 12. 2009 17:45

    Zuzka (neregistrovaný)

    Jenže vy jste je nezkoušel, já ano. Neznám žádný skutečně použitelný.

  • 16. 12. 2009 17:23

    logik (neregistrovaný)

    Hmmm, taky jsem hrozně dlouho hájil C++ – než jsem narazil na blbou implementaci C++ dědičnosti „pseudo“ interfaců:

    class IA
    {
    virtual void a()=0;
    };
    
    class IB: public IA
    {
    virtual void b()=0;
    };
    
    class A: public IA
    {
    void a() {};
    };
    
    class B: public A, public IB
    {
    void b() {};
    };
    
    
    int main()
    {
            new B();
    }

    (Při kompilaci to hodí:)

    [logik@atiks ~/tmp]$ gcc a.cpp
    a.cpp: In function 'int main()':
    a.cpp:24: error: cannot allocate an object of abstract type 'B'
    a.cpp:17: note:   because the following virtual functions are pure within 'B':
    a.cpp:3: note:  virtual void IA::a()

    Přitom jde o úplně triviální případ, kdy rozšířim rozhraní, a chci patřičně rozšířit i objekt – a c++ mě nutí zopakovat i metody rodičovského rozhraní. Nebo je snad nějakej jinej způsob, jak to rozumně obejít?
    (Dědičnost mezi IA a IB opravdu chci, protože chci aby tipová kontrola zajistila, že objekt rozhraní IB má i funkce z IA).

  • 17. 12. 2009 6:50

    ondra.novacisko.cz (neregistrovaný)

    Máte to špatně

    class IA
    {
    virtual void a()=0;
    };
    
    class IB: virtual public IA
    {
    virtual void b()=0;
    };
    
    class A: virtual public IA
    {
    void a() {};
    };
    
    class B: public A, public IB
    {
    void b() {};
    };
    
    
    int main()
    {
            new B();
    }

    Proč to tak je? Studujte:
    http://www.parashift.com/…ritance.html




    Ano. C++ je určitý kompromis mezi výkonem a strukturou. Tohle je jedno z technických omezení. Normálně se virtual nepoužívá, protože obecně je pomalejší, ale nikdo vám nebrání všechno dědit virtuálně, pak nebudete mít takové problémy. Ale počítejte s horším výkonem.

  • 18. 12. 2009 12:37

    logik (neregistrovaný)

    O možnosti virtuální dědičnosti kupodivu vím. Ale vždyť to píšete sám – virtuální dědičnost se nepoužívá, protože je pomalá. Dědit virtuálně všechno (ono by stačili včechny interfacy) sice lze, ale při složitější typový hierarchii by byl overhead při volání metod hroznej. Taky jsem v životě nikdy nesyšel doporučení, že by se to tak mělo dělat…

    Takže dědit virtuálně jen něco: a tady je právě ten problém. Kdyby to šlo vyřešit virtuální dědičností u objektu B, tak to ještě beru – v okamžiku návrhu třídy B vim, že dojde k diamantu a můžu to řešit. Ale tady to musím řešit při vytváření tříd A a IB. Tedy na naprosto nesouvisejícím místě kódu, což je zaprve v rozporu s principy OOP (každej objekt „kopá sám za sebe“) a jednak (nebo spíš proto) je to programátorsky velmi nepřijemný.
    A to už vůbec nemluvim o tom, že rozhraní IB nebo objekt A nemusí bejt navržený mnou (knihovna, jinej tým ve velký firmě atd…) a tedy nemusím mít možnost ho změnit…

    A navíc, i když tedy budu virtualizovat dědičnost jen tam, kde to je potřeba, tak stejně ten overhead může bejt poměrně dosti velkej a bude se (narozdíl od jinech jazyků) zvyšovat se složitostí programu (typovýho systému).

    Tydle dvě vlastnosti – narůstající overhead a nutnost nelokálních modifikací kódu mě myslím opravňují k tvrzením, že systém dědičnosti v C++ je horší, než v jiných programovacích jazycích.

    PS: Odkaz jsem nečet celej, ale jen z kusu, co jsem se kouknul bych ho až tak nedoporučoval. Technicky je sice dobře, ale tvrdit, že´auto je speciální případ motoru je blbina – to učí programátory „pragmatismu“ a ten se velmi často nevyplácí…

  • 18. 12. 2009 14:21

    ondra.novacisko.cz (neregistrovaný)

    Budu pokračovat v diskuzi, až si to přečtete celé…!!!! (močící pes)

    K virtuální dědičnosti… psal jsem to pod kódem. Je to kompromis mezi pravidly a implementací. Mezi výkonem a čistotou. K plnému OOP programování potřebujete virtuální stroj a intepretovaný kód. Současné CPU bohužel nemají podporu OOP, jsou to jen sčítací a násobící stroje přistupující na bajty v paměti. Až budou CPU nativně zvládat objekty, pak se pojďme bavit o čistém OOP. Zatím si musíte pomoci s největšími zloději výkonu… virtuálními stroji a frameworky. A nebo něco oželit.

    PS: Vhodnou strukturou se tohle dá obejít. Například tak, že už při návrhu počítáte s diamantovým děděním. Například vstupní stream a vystupni stream deklarujete virtualni, protože předpokládate použití pro vstupně výstupní stream.

    Nebo využijete šablon.

    Klidně Vám nabídnu konzultace, kontakt viz adresa v mé přezdívce.

  • 18. 12. 2009 17:24

    logik (neregistrovaný)

    Je mi jedno, jestli jazyk běží nebo neběží nad virtuálním strojem, chci, aby to bylo rychle. btw. díky JIT a optimalizacím již příliš neplatí, že bytecode jazyky musí bejt pomalejší, pomalu se na čistě kompilovanej kód dotahujou a
    někdy (díky možnostem optimalizace za běhu, které se nedají provést během kompilace) jsou i rychlejší.
    A tendle trend bude pokračovat – stejně jako dřív byly šachy na počítači zoufale slabý, teď již sváděj souboje s velmistrama a pomalu nabývaj vrchu, stejně tak i neni daleko doba, kdy počítač udělá optimalizace kódu přinejmenším stejně dobře jako programátor. Ale to je vlastně trochu vedlejší věc, jen reaguju na to, že virtuální stroj je zloděj výkonu…

    Fakt je, že snad všechny moderní jazyky včetně těch, které žádnej virtuální stroj nemají (např. i „blbej“ object pascal) zvládaj diamantovou dědičnost interfaců lépe a ve výsledku rychleji (nemusí řešit x-úrovňový virtuální dědičnosti) než C++. To prostě není záležitost virtuálního stroje, to je špatnou implementací vícenásobný dědičnosti v C++.
    Např. nejjednodušší metoda jak to vyřešit by byla, kdyby kompilátor zopakoval ve VMT pro nově poděděný interface implementovaný metody z předků těch interface – klidně i volitelně nějakým klíčovým slovem.

    Šablon jsem právě pro obejití tohodle využíval – ale to je s odpuštěním trochu prasečina. Teď už bych na to radši použil tu virtuální dědičnost, je to čistší.

    A co se týče toho, že se to vhodnou strukturou dá obejít a že při návrhu počítám s diamantovym děděním – to je právě pointa mého předchozího příspěvku – že to je právě to, proč říkám, že C++ není příliš vhodný pro objektové programování. Protože mě nutí při návrhu jedné třídy modifikovat jiné třídy, ke kterým notabene ani nemusím mít přístup a nutí mě myslet na to, co by za mne mohl v pohodě udělat kompilátor (a co taky u ostatních jazyků kompilátor dělá).

  • 18. 12. 2009 20:02

    ondra.novacisko.cz (neregistrovaný)

    „Např. nejjednodušší metoda jak to vyřešit by byla, kdyby kompilátor zopakoval ve VMT pro nově poděděný interface implementovaný metody z předků těch interface – klidně i volitelně nějakým klíčovým slovem.“

    A že tam máte tu base 2× to vám je jedno, že? A co když do ty base date member proměnnou? Co potom? A co když jí tam nedáte vy, ale někdo, komu ta knihovna patří?

    Programujte jako v Javě. Diamantovou dědičnost převeďte na volání delegátů a máte to.

  • 19. 12. 2009 2:19

    Logik

    Jakou bází mám kde dvakrát? Myslíte tu část objektu definovanou v IA? Dvakrát nula je furt nula. Tadle „duplicita“ ničeho mě opravdu netrápí.

    Jediný, co tam bude dvakrát, budou odkazy ve VMT na funkce definované v interfacu IA. Ty budou jednou v části VMT (třídy B) příslušné k IA a jednou v části IB. Obojí to budou ale funkce nad stejnými a jedinečnými daty objektu A, takže žádný problém.
    Bude se to chovat naprosto stejně, jako kdybych pro všechny funkce v IA v objektu B napsal wrapper volající patřičné funkce v A (což je právě chování jako mají všechny ostatní jazyky).

    A co kdyby IA drželo nějaká data, moh by kompilátor klidně zařvat (tato featura je přeci třeba pro interface) koneckonců teď zařve tak jako tak, takže by to nic nezkazilo.

    Poslední větu o Javě už nechápu zcela. Zaprve v javě jsou interfacy a chovají se korektně, takže nevím, proč bych tam měl programovat jinak, zadruhé v Javě co vím ani delegáti nejsou.

  • 19. 12. 2009 21:31

    ondra.novacisko.cz (neregistrovaný)

    „Jakou bází mám kde dvakrát? Myslíte tu část objektu definovanou v IA? Dvakrát nula je furt nula. Tadle „duplicita“ ničeho mě opravdu netrápí.“

    Nabízím konzultace nebo výuku programování v C++, kontaktujte mne na adrese ondra.novacisko.cz.

    V rámci sezení proberemi i technické informace o implementaci objektů v C++, i proč nevirtuální dědění znamená duplikace instancí,které většinou nejsou ku prospěchu věci. Probereme si i to, jak je implementováno virtuální dědění a kdy stojí výkon a kdy nikoliv.

    Ceny nejsou vysoké.

  • 21. 12. 2009 16:59

    Logik

    LOL – přestaňte nhabízet ty vaše vypečený semináře a konzultace (kupodivu vím, jak je v C++ implementovaná dědičnost už od svých cca 13 či 14 let) a zkuste si zopakovat, co to je INTERFACE (alespoň tedy v C++ podání). Interface jaksi NEMÁ žádné proměnné a tedy v případě interfaců x-násobnost jejich výskytu opravdu nevadí. (a proč bych to měl v OO jazyku vůbec řešit??)

    Ale už nemám energii se hádat s člověkem, který neustále nabízí konzultace v C++, tvrdí že programuje objektově a přitom tvrdí, že kočka se skládá z kočky a savce. Já teda veřejně prohlašuju, že se rozhodně z člověka a savce neskládam a Vy se skládejte z čeho chcete, a mimo interfaců si zopakujte, jakej je rozdíl mezi agregací a dědičností.

    Omlouvám se, že reaguji takto, ale to, že vás každou chvíli někdo nachytá při tom, že tvrdíte blbost (kromě onoho skládání objektů např. rychlost volání virtuálních metod v C#, delegáti v javě, evidentně nevíte co se běžně nazývá typ) a přitom furt arogantně nabízíte své konzultace a navíc za peníze.

    Tak Vám přeji ať se Vám ve Vašem C++ programuje dobře a omlouvám se, že už nebudu reagovat, asi nemá to cenu.

  • 16. 12. 2009 21:16

    xx (neregistrovaný)

    Například omezenost typového systému těžko vyřešíte knihovnou. Stejně jako problémy se šablonami.

    Nebo si vemte „nebezpečné“ konstrukce jako přetypování nebo ukazatelová aritmetika, těch se bohužel nezbavíte.

  • 17. 12. 2009 7:01

    ondra.novacisko.cz (neregistrovaný)

    A kladivem se můžete bouchnout do prstu. Stejně vám nikdo nezabrání strkat jehlice do zásuvky i přesto, že tam je kryt. A když tam budete mít dětskou pojistku, přesto to nezabrání tam strčit dvě jehlice.

    C++ je sada nástrojů k práci. To co z toho budete používat je čistě na Vás. Nikdo Vás nenutí používat „nebezpečné“ konstrukce jako přetypování, nebo ukazatelovou aritmetiku. Prostě ji nepoužívejte. Ale být tam musí, až začnete psát něco hodně low-level, tak Vám nic jiného nezbyde.

    Nevím, jaké problémy se šablonami myslíte? Kdy jste naposledy viděl C++? Typuju tak před 10 lety? On je dávno vývoj někde jinde. Oba dominantní hráči (MSVC a GCC) implementují šablony z 99% podle normy a z 99.99999% jsou v zájemně kompatibilní, a pouze fajnšmekr může občas narazit na komplikace. Ale mohu Vám garantovat, že už jsem kompilátorem prohnal kde jakou komplikovanou několikrát rekurentní šablonu s mnoha větvemi ve stylu „programování v PROLOGu“, kolikrát s tím „že tohle snad už ani nemůže fungovat“. Fungovalo, tak jak mělo, v obou překladačích.

  • 17. 12. 2009 9:27

    xx (neregistrovaný)

    > Nevím, jaké problémy se šablonami myslíte?

    Uvedu je:

    1) Mohu napsat takové šablony, že je vše v pořádku, dokud je neinstanciuji. Prostě do té doby to kompilátor není schopen rozumně zkontrolovat.

    2) Pro každý typ, co dám do šablony, se generuje nový kód. Tedy v některých případech může výrazně narůst množství kódu.

    3) Když děláte šablonovou knihovnu a chcete ji prodávat tak, aby nikdo neviděl implementaci, tak máte smůlu, protože veškerý kód je v hlavičkových souborech a ty vydat musíte.

    Často se šablony používají tam, kde stačí generika z jazyků jako C#, Java, … A v těchto jazycích odpadají problémy 1) a 3) a někdy i 2). Tam, kde je potřeba dělat něco rafinovanějšího než umí generika, mi přijde lepší použít mechanismy jako je Template Haskell, tj. mohu spouštět svůj kód v době překladu a pomocí něj generovat nový kód.

    > Nikdo Vás nenutí používat „nebezpečné“ konstrukce jako přetypování, nebo ukazatelovou aritmetiku. Prostě ji nepoužívejte. Ale být tam musí, až začnete psát něco hodně low-level, tak Vám nic jiného nezbyde.

    No, tak tomu přetypování by se v některých případech dalo vyhnout, pokud by C++ mělo silnější typový systém. Další nevýhoda ukazatelové aritmetiky je v tom, že ztěžuje optimalizaci.

  • 17. 12. 2009 17:47

    ondra.novacisko.cz (neregistrovaný)

    1) Ano, máte pravdu, ale to je princip šablon. Už to slovo „šablona“ vymezuje její použití. Nemůžete prostě napsat fakturu do šablony objednávky. Ale šablona je v pořádku. Pokud narážíte na Visual Studio, který neprovádí parsování šablony před instanciováním, tak to je nevýhoda Visual Studia, nikoliv C++. GCC šablony poctivě kontroluje před instanciováním. Chyby po instanciování vyplývají s chybného použití šablony, ne v chybě šablony (z hlediska překladače)

    Při ladění si můžete šablonu explicitně specializovat, třeba template MojeSablona<int>. A tím padá jakýkoliv argument na to, že nejste schopen to zkontrolovat.

    2) Nemáte pravdu! Šablona se na venek tváří jako separátní kód a je to správně. Protože výsledný kód pro jednu třídu může být úplně jiný než pro jinou třídu. Optimalizační mechanismy však mohou způsobit, že se kód nakonec nasdílí, pokud je podobný. Nebo naopak, výsledkem je efektivnější kód napsaný na míru dosazené třídy, namísto aby kód řešil až v runtime typ třídy a zbytečně zajišťoval krajní případy

    3) Mno, tak nemusíte. Můžete těla šablon umístit do souboru cc. Šablony pak explicitně instanciujete pro typy, které si vyberete. Nevýhodou je, že nemůžete pomocí těch šablon odvodit kód pro další typy, které by chtěl použit uživatel, jednoduše proto, že potřebujete původní zdroják.

    Genetika C# a v Java je pouze kamufláš. Ve skutečnosti je to jen implicitní přetypování vycházející z předpokladu, že každý objekt je potomkem nějakého generického objektu. Kód se rozhodně neoptimalizuje na zadaný typ. Právě proto můžete říct, že lze 1) 3) a možná 2). Protože to nejsou šablony, jen taková hračka. Fake. Syntaxtická berlička, nikoliv pravá generika.

    Template Haskell bych už zase neřadil do C++, code generátory lze používat mimo C++. Výhoda šablon je, že překladač umí pracovat s interními informacemi tříd a optimalizovat kód jim na míru. Pomocí šablon můžete i napsat kód, který se provede v době překladu. I když je trochu omezený, tak jeho síla je obrovská. Je to totiž deklarativní jazyk, něco jako PROLOG a kdo v něm umí chodit, zvládne v tom programovat tak, že překladač během jednoho běhu přeloží kód který si sám vygeneruje v rozsahu několika desítek tisíc řádků.

    Nemusíte víc dokazovat že šablonám nerozumíte. Nikdy jste v tom nic pořádně nedělal. A pokud jo, tak se to nesnažte dokázat člověku, který se šablonami zabývá přes 10 let a poslední dobou značně nadstandardě.

    „No, tak tomu přetypování by se v některých případech dalo vyhnout, pokud by C++ mělo silnější typový systém. Další nevýhoda ukazatelové aritmetiky je v tom, že ztěžuje optimalizaci.“

    Přestaňte dokazovat, že jste C++ viděl z rychlíku. C++ má dost silný typový systém a přetypování v zásadě nepoužívám (myšleno klasické Cčkovské). Přetypováním nemyslím dynamic_cast a static_cast případně const_cast, protože se defacto nejedná o přetypování, ale pouze pomůcky řešící zejména implementační obtíže OOP adaptovaného přímo do efektivního kódu procesoru (žádný virtuální stroj).

    Ukazatelová aritmetiku také nepoužívám. Nemusíte to používat.

  • 17. 12. 2009 18:56

    xx (neregistrovaný)

    > C++ má dost silný typový systém a přetypování v zásadě nepoužívám (myšleno klasické Cčkovské). Přetypováním nemyslím dynamic_cast a static_cast případně const_cast, protože se defacto nejedná o přetypování, ale pouze pomůcky řešící zejména implementační obtíže OOP adaptovaného přímo do efektivního kódu procesoru (žádný virtuální stroj).

    Ale jedná se o přetypování. Například dynamic_cast často potřebujete proto, že v C++ není možné dělat kontravariantní změny v typech parametrů metod.

    Další omezení C++ je, že podtřída je vždy zároveň i podtyp.

    > Genetika C# a v Java je pouze kamufláš. Ve skutečnosti je to jen implicitní přetypování vycházející z předpokladu, že každý objekt je potomkem nějakého generického objektu.

    Pro C# toto neplatí, tam se kód pro instance generik generuje za běhu programu JITem a ten dokáže také sdílet různé části kódu. Nemluvě o tom, jak pohodlně se takový program ladí.

    > Pomocí šablon můžete i napsat kód, který se provede v době překladu. I když je trochu omezený, tak jeho síla je obrovská. Je to totiž deklarativní jazyk, něco jako PROLOG a kdo v něm umí chodit, zvládne v tom programovat tak, že překladač během jednoho běhu přeloží kód který si sám vygeneruje v rozsahu několika desítek tisíc řádků.

    Tak Template Haskell je právě o tom, že kód běží na úrovni překladače, ale
    s tím rozdílem, že je to plnohodnotný Haskell. Zato šablony v C++ jsou mnohem omezenější než celé C++. A s Prologem bych to až tolik nesrovnával, protože ten má backtracking.

    > Template Haskell bych už zase neřadil do C++,

    Nemyslel jsem zařadit Template Haskell do C++, ale udělat do C++ něco podobného tj. třeba možnost psát funkce v C++, které se budou vykonávat v době překladu a budou moci generovat kód.

    > 2) Nemáte pravdu! Šablona se na venek tváří jako separátní kód a je to správně. Protože výsledný kód pro jednu třídu může být úplně jiný než pro jinou třídu.

    To sice může, jenže často je téměř stejný (až na typy).

  • 18. 12. 2009 14:43

    ondra.novacisko.cz (neregistrovaný)

    Na C# potřebujete virtuální stroj a framework. Tam se realizuje OOP lépe, protože každý objekt obsahuje runtime databazi metod. Proto nepotřebujete přetypovávat, protože pokud objekt nepodporuje metodu, nebude nalezena v tabulce a program skončí s výjimkou.

    V C++ nic takového není… naštěstí nebo bohužel? V C++ je virtuální metoda realizována pomocí indexu v tabulce adres s přístupem se složitostí 1 (v C# log N). Nevirtuální metoda dokonce má složitost nulovou, protože je to jen skok na adresu (v obou případech procesor není přetížen flushováním front). Volání metody v C++ tak bude vždy rychlejší než v C#, i kdyby byl překladač JIT 1000× lepší, než GCC

    dynamic_cast není přetypování, je to jen přechod předka na potomka. V žádném případě nedojde ke změne objektu, pořád se pracuje se stejným objektem, který po dynamic_castu umí to co uměl předek, z něhož se vycházelo. static_cast je stejný jako dynamic_cast, pouze se provádí při překladu, pokud je převod znám a autor programu převzal zodpovědnost za to, že referencí je skutečně objekt, který uvedl.

    Všechno tohle je dáno tím, jak je C++ implementován. Je skoro zázrak, že C++ zvládne techniky OOP i přesto, že výsledkem je nativní kód bez nutosti podpory ze stran velkých frameworků a s tím, že valná většina OOP technik má přímou transformaci do nativního kódu.

    „Nemyslel jsem zařadit Template Haskell do C++, ale udělat do C++ něco podobného tj. třeba možnost psát funkce v C++, které se budou vykonávat v době překladu a budou moci generovat kód.“

    Současné C++ šablony umí vykonávat kód v době překladu. Není to tak přímočaré, ale kdo to umí, tak mu to nedělá problémy.

    „A s Prologem bych to až tolik nesrovnával, protože ten má backtracking.“
    Ale je tam, sic omezený. Nicméně pokud některou funkci nelze instanciovat pro syntaxtickou chybu v parametrech, tak překladač vybere jiného kandidáta. S PROLOGem má společný hlavně deklarativní zápis a způsob výběru pravidel včetně jejich specializací. Backtracking by se hodil, ale bez něho se taky obejdu. Prostě cesty které nevedou k výsledku zaříznu už na úrovni specializace, případně v místě rozhodování pošlu překladač do jiného pravidla.

    „To sice může, jenže často je téměř stejný (až na typy)“
    Vidím, že znáte C++ líp než ja :-D Haha.

  • 18. 12. 2009 15:18

    xx (neregistrovaný)

    > V C++ nic takového není… naštěstí nebo bohužel? V C++ je virtuální metoda realizována pomocí indexu v tabulce adres s přístupem se složitostí 1 (v C# log N). Nevirtuální metoda dokonce má složitost nulovou, protože je to jen skok na adresu (v obou případech procesor není přetížen flushováním front). Volání metody v C++ tak bude vždy rychlejší než v C#, i kdyby byl překladač JIT 1000× lepší, než GCC

    To je nesmysl. C# to realizuje úplně stejně jako C++ (s výjimkou klíčového slova dynamic).

    > dynamic_cast není přetypování, je to jen přechod předka na potomka.

    A když ten potomek má jiný typ než předek, tak je to přetypování.

    > V žádném případě nedojde ke změne objektu, pořád se pracuje se stejným objektem, který po dynamic_castu umí to co uměl předek, z něhož se vycházelo.

    Přetypování je o změně typu a ne o změně objektu.

    > Všechno tohle je dáno tím, jak je C++ implementován. Je skoro zázrak, že C++ zvládne techniky OOP i přesto, že výsledkem je nativní kód bez nutosti podpory ze stran velkých frameworků a s tím, že valná většina OOP technik má přímou transformaci do nativního kódu.

    Nic moc zázračného na tom nevidím. Eiffel má silnější typový systém a překládá se do C.

  • 18. 12. 2009 15:46

    ondra.novacisko.cz (neregistrovaný)

    „To je nesmysl. C# to realizuje úplně stejně jako C++ (s výjimkou klíčového slova dynamic).“

    Nesmysl píšete Vy.

    „A když ten potomek má jiný typ než předek, tak je to přetypování.“

    Máte v tom chaos. Nemotejte dohromady typy, třídy a objekty. Typ je jen syntaxtická berlička. Podle tohoto pohledu se samozřejmě přetypovává i v Javě. Jedna věc je změna typu „ukazatele“ tedy změna pohledu na stejnou věc, druhá je změna věci jako celku. Přetypování z int na float je ten druhý případ a to není případ dynamic_castu.

    „Nic moc zázračného na tom nevidím. Eiffel má silnější typový systém a překládá se do C.“

    Otázkou je, zda existuje přímá transformace. Zkuste někdy analyzovat výstup Eiffelu, třeba tam najdete dosti hustý framework.

    Nevím co máte s typovým systémem. z hlediska OOP jsou typy spíš přítěží než výhodou.

  • 18. 12. 2009 16:44

    xx (neregistrovaný)

    > Nesmysl píšete Vy.

    Ve 4. odstavci je napsáno, že C# používá pro virtuální metody také vtable:

    http://en.wikipedia.org/…method_table

    > Máte v tom chaos. Nemotejte dohromady typy, třídy a objekty. Typ je jen syntaxtická berlička.

    Já se obávám, že v tom máte zmatek Vy. Typ není žádná syntaktická berlička.

    > Podle tohoto pohledu se samozřejmě přetypovává i v Javě.

    Tvrdím snad, že ne? Problém ale je, že existuje řada případů, kde by se tomu dalo vyhnout, jenže kvůli omezenosti typového systému to nejde a musíte použít přetypování (ta omezenost se týká i Javy).

    > Nevím co máte s typovým systémem. z hlediska OOP jsou typy spíš přítěží než výhodou.

    Typy (ve staticky typovaných jazycích) mohou výrazně dopomoci ke správnosti programu a informace z typového systému může být využita kompilátorem při optimalizaci programu. To, že dnes nejpoužívanější OOP jazyky jsou dnes 20 let nebo ještě více pozadu za vědeckými poznatky ještě neznamená, že typy jsou přítěží.

  • 18. 12. 2009 17:23

    ondra.novacisko.cz (neregistrovaný)

    „Já se obávám, že v tom máte zmatek Vy. Typ není žádná syntaktická berlička.“

    Je. OOP nezná typ objektu. OOP zná jen objekt. Dokonce OOP nezná ani třídu. OOP je pouze o objektech, které přijímají zprávy. Typ slouží jen k tomu, aby překladač poznal dopředu, podle jakých pravidel se bude jednat.

    Typ definuje protokol, interface, kanal, pohled (view), syntaxtická berlička. Přetypování, jak vy říkáte, je jen změna protokolu, interface, kanalu, pohledu. Normální akce, bez které se neobejdete. Vyplývá to z logiky věci. A to jestli překladač umí přetypovat automaticky, nebo se mu to musí říct ručně, to už je vedlejší.

    Nevím, jak by vypadal svět s dokonalým typovým systémem. Zkuste nahodit příklad. Nechci žádný odkaz. Svými slovy.

  • 18. 12. 2009 17:52

    xx (neregistrovaný)

    > Nevím, jak by vypadal svět s dokonalým typovým systémem. Zkuste nahodit příklad. Nechci žádný odkaz. Svými slovy.

    Správně typovaný program skončí (na konečném vstupu) a nedojde v něm k běhové chybě (pokud je dostatek paměti).

    > Je. OOP nezná typ objektu. OOP zná jen objekt. Dokonce OOP nezná ani třídu. OOP je pouze o objektech, které přijímají zprávy. Typ slouží jen k tomu, aby překladač poznal dopředu, podle jakých pravidel se bude jednat.

    Já si pod OOP představuji to, co je popsáno v knize „Foundations of Object-Oriented Languages: Types and Semantics“, kde jsou objekty, třídy a typy. Ale jsem si vědom toho, že někteří autoři neuznávají třídy, nicméně nevím, že by někdo neuznával typy – nedovedu si představit formalismus, který by neznal pojmy typ a podtyp – AFAIK běžně se to používá – už jen kvůli definici polymorfismu.

  • 19. 12. 2009 13:49

    Logik

    To co píšeš (xx) bych 100% podepsal.
    Jinak virtuální metody jsou v C# rychlejší než v C++, protože v nezanedbatelným počtu případů je umí JIT díky analýze kódu za běhu inlinovat, popř. nahradit statickym voláním.

  • 19. 12. 2009 21:34

    ondra.novacisko.cz (neregistrovaný)

    Ještě tu o Červené Karkulce. Nebo už vás nakazili ekologové a budete tvrdit, že nejlevnější energie je z přírodně obnovitelných zdrojů.

    Předpokládám, že nějaké oficiální srovnání, nebo aspoň nějaký hmatatelný důkaz určitě nemáte.

  • 21. 12. 2009 16:55

    Logik

    Např.
    http://blogs.microsoft.co.il/…N_2900_.aspx
    (neni to ten článek, kterej sem hledal, ono je těch technik daleko víc a sofistikovanějších, užívá jich např. co tuším Self).

    Výkonnostní srovnání Javy versus C++ třeba tady:
    http://kano.net/javabench/
    http://www.stefankrause.net/wp/?p=6
    atd…
    Nechci tím říkat, že je Java rychlejší, jen dokumentuju, že někdy může být rychlejší.