Alternativa pro referenci na NULL – C / C++ – Fórum – Programujte.com
 x   TIP: Přetáhni ikonu na hlavní panel pro připnutí webu
Reklama
Reklama

Alternativa pro referenci na NULL – C / C++ – Fórum – Programujte.comAlternativa pro referenci na NULL – C / C++ – Fórum – Programujte.com

 

Toto vlákno bylo označeno za vyřešené — příspěvek s řešením.
Hledá se programátor! Plat 1 800 € + bonusy (firma Boxmol.com)
Doomista+1
Stálý člen
14. 9. 2016   #1
-
0
-

 Zdravím,

pokud mám funkci, která vrací konstantní referenci na nějaký objekt

const Object &getObject() const;

tak pokud by vracela ukazatel a z libovolného důvodu by nemohla vrátit validní ukazatel, vrátila by null. Co má ale v případě chyby vracet, když její návratová hodnota je const& ? Jaký je industry standard pro tento případ?

Mám si pro každý datový typ, u nějž hrozí vrácení chyby, vytvořit nějaký dummy objekt, který by znamenal "toto je chyba" a porovnávat, zda jsou adresy stejné?

Díky za odpovědi

Nahlásit jako SPAM
IP: 2001:67c:1220:809::93e5:9...–
Na vše stačí iostream...
Reklama
Reklama
Řešení
KIIV+42
God of flame
14. 9. 2016   #2
-
+2
-
Zajímavé
Vyřešeno Nejlepší odpověď

Ano, muze pouzit null object pattern, nebo pokud je to chybovy stav, tak muze hodit vyjimku.

Nahlásit jako SPAM
IP: 212.47.3.–
Program vždy dělá to co naprogramujete, ne to co chcete...
Kit+11
Guru
14. 9. 2016   #3
-
0
-

#2 KIIV
NullObject pattern je pro někoho až moc objektový, ale je to správné řešení.

NullObject se dobře implementuje přes Singleton.

Nahlásit jako SPAM
IP: 2a00:1028:83a0:37a6:7050:...–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
Staon0
Newbie
14. 9. 2016   #4
-
0
-

#1 Doomista
NullObject je sice hezký, ale jeho reálné užití obecně příliš praktické není. Nebudete přece pro každé sebeprťavější rozhraní, které si v programu vytvoříte, implementovat jeho NullObject jenom proto, aby následně bylo možné ho vracet z funkcí. Je to dost pracné, a navíc naimplementovat metody rozhraní v NullObject tak, aby dávaly nějaký smysl, může být dost náročná až nemožná práce. (Pokud je nenaimplementujete, tak stejně nakonec bude muset klientský kód porovnávat je to/není to null, čímž se výhoda NullObject vzoru ztrácí.)

Řešením je, jak píše KIIV, vyhodit výjimku, pokud se skutečně jedná o chybový stav.

Pokud se nejedná o chybový stav, tedy možnost, že objekt nebude nalezený, je běžné chování, pak skutečně vracejte obyčejný ukazatel. Už samotná přítomnost ukazatele programátora nutí si uvědomit, že může dostat nullptr a popasovat se s tím. Nevím, jestli se to dá nazvat jako průmyslový standard, ale viděl jsem to všude (narozdíl od NullObject) a moje praktické zkušenosti s tím mám velmi dobré.

Nahlásit jako SPAM
IP: 94.112.135.–
Kit+11
Guru
15. 9. 2016   #5
-
0
-

#4 Staon
Těch metod v rozhraní nebývá tolik, aby implementace v NullObject byla nějak významně složitá. Těch 3-5 metod se dá zvládnout docela snadno.

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.
Staon0
Newbie
15. 9. 2016   #6
-
0
-

#5 Kit

  1. Pokud implementujete jednu třídu navíc s 3 až 5 metodami pro každé rozhraní, které máte, tak je to sakra práce navíc.
  2. I implementace 3 metod může být dost velký oříšek, pokud se nedá pořádně nadefinovat, co by vlastně takový neexistující objekt měl dělat. Asserty a výjimky nejsou obecně řešení, protože pak nutíte klientský kód si stejně nejdříve zjistit, zda se nejedná o NullObject (a to už si můžete rovnou porovnat normální ukazatel).
  3. Jak jste přišel na to, že 3-5 metod je běžná velikost rozhraní? Ve svém okolí bych průměr hádal tak kolem 8 až 10 metod a občas jsou k vidění i rozhraní s několika desítkami metod, aniž by narušovaly Single Responsibility.

Nahlásit jako SPAM
IP: 94.112.135.–
Kit+11
Guru
15. 9. 2016   #7
-
0
-

#6 Staon

  1. Práce navíc u NullObject se mi projeví ušetřením na klienském kódu. V konečném důsledku ušetřím, protože klienský kód neporuší SRP.
  2. Implementace běžné třídy mi obvykle zabere 1-2 hodiny včetně testů. NullObject mám naimplementovaný rychleji.
  3. V rozhraní nemívám víc než 5 metod, obvykle jen 2-3. Nevidím důvod, proč by třída měla mít desítky veřejných metod. To už zavání porušením zapouzdření.

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

Díky za všechny názory. Staonova poznámka, že je občas celkem těžké vymyslet, co takový NullObject musí udělat, mi přijde celkem na místě, protože pokud program pokračuje dál, jako by se nechumelilo, tak by ten NullObject měl asi nějak dát najevo, že se vyskytl (aspoň error logem), a že ho tam pravděpodobně nechceme.

Házet výjimku se mi úplně nechce, v případě chybějícího klientského try and catch by ten program lehnul tak jak tak, nebo se pletu?

Nahlásit jako SPAM
IP: 2001:67c:1220:809::93e5:9...–
Na vše stačí iostream...
Kit+11
Guru
15. 9. 2016   #9
-
0
-

#8 Doomista
Spíš je otázka, co chceš aby ten NullObject dělal. Může dělat cokoli a vůbec to nemusí být chybový stav.

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.
Doomista+1
Stálý člen
15. 9. 2016   #10
-
0
-

  Jasně, to už pak záleží na kontextu. Každopádně díky za pomoc

Nahlásit jako SPAM
IP: 2001:67c:1220:809::93e5:9...–
Na vše stačí iostream...
Staon0
Newbie
15. 9. 2016   #11
-
0
-

#7 Kit

  1. Právě že obecně neušetří. Pokud budu mít nějakou vyhledávací strukturu (řekněme telefonní seznam), tak je zcela korektní a není chybovým stavem, že daný klíč v seznamu není. Daný člověk jednoduše nemá telefon. Klientský kód se v tomto případě bude skutečně chovat rozdílně pro situaci "našel" a pro situaci "nenašel" (někdy zobrazí dialog, někdy člověka přeskočí a nepošle mu spam SMS, atd.). Jak vytvoření něčeho jako NullPersonRecord v tomto případě ušetří práci? Klientský kód stejně musí výsledek nějakým způsobem porovnat a rozskočit se. Možnost, že by alternativní kód byl přímo v NullPersonRecord je čuňárna. Zaprvé porušíte single resonsibility, protože taková funkce do záznamu o uživateli logicky nepatří. Zadruhé půjde o velmi nepraktické a nerozšiřitelné řešení, protože budete muset přenést nesouvisející kód ze všech míst, která pracují s telefonním seznamem, na jedno místo.
  2. Pokud se nedá pořádně určit, co by měl null object dělat, tak rozhodně to rychleji mít nebudete. Navíc jeho implementace není jediné zdržení. V C++ totiž musíte nějak zajistit vytvoření instance null object tak, aby existovala i po opuštění funkce a mohl jste ji bezpečně vrátit. Pokud to uděláte jako globální proměnnou/konstantu, tak musíte zaručit, že ji nikdo nebude chtít nikdy použít před vstupem do main, protože pořadí dynamické inicializace modulů není definované. Pokud použijete nějakou formu lazy inicializace, tak se situace zase komplikuje kvůli synchronizaci vláken. (Připouštím, že C++11 hodně pomáhá, když už má inicializaci statických proměnných ve funkci synchronizovanou. Ale podpora C++11 ještě pořád není na všech překladačích, takže když se na to spolehnete, nebudete mít přenositelný kód.)
  3. Jak konkrétně bohaté rozhraní narušuje zapouzdření?

Nechápejte mě špatně, já proti NullObject nic nemám. Jsou situace, kdy se perfektně hodí a dokáže úžasným způsobem zjednodušit a zpřehlednit kód. Ale v žádném případě to není obecně platná odpověď na otázku "co vracet, pokud objekt neexistuje."

Nahlásit jako SPAM
IP: 94.142.234.–
Staon0
Newbie
15. 9. 2016   #12
-
0
-

#8 Doomista
Ano, to je přesně to, co jsem měl na mysli výrokem "výjimky nepomohou." Pokud klientský kód výjimku neočekává, může to dělat pěkné brikule včetně pádu procesu.

Ale ani situace, kdy ji očekává není dobře. Vyhozená výjimka znamená chybový stav, takže se nabízí otázka, proč nevyhodit výjimku už při hledání objektu, jestliže nenalezení objektu je chybový stav. Takhle efektivně hlášení programu o chybovém stavu odsunete daleko od místa, kde k chybě skutečně došlo. A zkuste si odladit program, kde se vám chyby budou projevovat tisíce instrukcí od místa jejich vzniku 

Nahlásit jako SPAM
IP: 94.142.234.–
Kit+11
Guru
15. 9. 2016   #13
-
0
-

#11 Staon

  1. Pro zmíněný telefonní seznam by NullObject nedával smysl. Pokud není nalezen hledaný záznam, je vrácena prázdná kolekce, se kterou se pracuje běžným způsobem.
  2. NullObject se běžně dělá jako Singleton a nikdy se nelikviduje.
  3. Bohaté rozhraní obvykle porušuje zapouzdření tak, že je v něm nasekána hromada různých getterů, setterů a podobných blbostí. Rozhraní má být tenké, aby byla minimální závislost mezi třídami a třídy tak byly pokud možno snadno zaměnitelné.

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.
Kit+11
Guru
15. 9. 2016   #14
-
0
-

#12 Staon
Výjimky běžně nechávám propadnout přes několik pater a vůbec mi to nedělá potíže s laděním aplikace. Typ a text výjimky obsahuje všechny informace, které pro její zpracování potřebuji, cestou se mi přidá i její kompletní trasa. Výjimku mohu, ale nemusím hlásit jako chybový stav - na patřičném levelu ji mohu zotavit do platného stavu (zde je prostor např. pro NullObject) a pokračovat v aplikaci.

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.
Staon0
Newbie
16. 9. 2016   #15
-
0
-

#13 Kit

  1. to je přesně to, o čem celou dobu mluvím. NullObject se hodí pouze někdy. Jsem rád, že jsme se shodli.
  2. Tím, že něco pojmenujete návrhovým vzorem, nijak neodstraníte problémy, které jsem popsal. Zkoušel jste si někdy implementovat singleton s double checked locking? Není to jednoduché, snadno tam uděláte chybu, a prakticky se to nedá ladit. Navíc singleton není v tomhle případě úplně to nejlepší filozoficky. Jeho hlavním cílem je zaručit maximálně jednu instanci, ale proč by NullObject obecně nemohl mít instancí víc?
  3. Nevšiml jsem si, že bych kdekoliv psal o getterech a setterech. Máte pravdu, že bohaté rozhraní může způsobovat těsné závislosti, ale vy jste mluvil o zapouzdření. Navíc ani settery a gettery zapouzdření neruší. (Čímž neříkám, že jsou dobře. Narušují něco, čemu já sám pro sebe říkám logické zapouzdření, normálně se mluví o anemických objektech.)

Nahlásit jako SPAM
IP: 94.142.234.–
Staon0
Newbie
16. 9. 2016   #16
-
0
-

#14 Kit
Tady jste vůbec nepochopil, o čem píšu. Výjimky samozřejmě propadávají vertikálně přes callstack. To je jejich normální vlastnost. Já ale ukazuji, že NullObject s metodami implementovanými výjimkami, způsobuje horizontální zpoždění. Výjimka vypadne z místa, které je vzdálené od místa, kde skutečně chyba nastala. V tomto příkladu chyba nastala tehdy, když nebyl nalezený záznam pro uživatele. Ale projeví se až bůhví kde při prvním přístupu k objektu. Vy pak sice ve výjimce vidíte, že chyba nastala při volání NullObject::něco(), ale už nemáte jak dohledat, proč se vůbec stalo, že jste dostal instanci NullObject.

Výjimky mají být vždy chybový stav. Jejich použití k něčemu jinému je obvykle špatně. To že se z chyby program umí na nějaké úrovni zotavit na tom nic nemění.

Nahlásit jako SPAM
IP: 94.142.234.–
Doomista+1
Stálý člen
16. 9. 2016   #17
-
0
-

Kolega právě přišel se zajímavým názorem, který se údajně (za sebe nevím, kde) často používá v std. Get funkce vrací true/false v závislosti zda (ne)našla objekt a naplní referenci, která byla předána jako parametr funkce.

Nahlásit jako SPAM
IP: 2001:67c:1220:809::93e5:9...–
Na vše stačí iostream...
KIIV+42
God of flame
16. 9. 2016   #18
-
0
-

#17 Doomista
to uz muzes rovnou vracet std::pair<bool, trida> ci iterator...

Nahlásit jako SPAM
IP: 212.47.3.–
Program vždy dělá to co naprogramujete, ne to co chcete...
Staon0
Newbie
16. 9. 2016   #19
-
0
-

#17 Doomista
Tohle řešení funguje pouze pro kopírovatelné objekty a objekty které mají default konstruktor. Referenci nelze přiřadit, pouze inicializovat. Takže aby to fungovalo, musíte si nejdříve vytvořit prázdnou instanci navraceného objektu a v get metodě do ní přiřadit.

Tohle je strašně velké omezení, aby se to dalo považovat za obecně platné řešení. Automaticky jste tak totiž vyřadil objekty, které se kopírovat neumí. Což jsou například všechny polymorfní typy, které dělat kopírovatelné je cesta do pekla. Navíc u polymorfních typů dopředu nevíte, jakou konkrétní implementaci vám to vrátí, takže ani nejste schopný udělat dopředu prázdnou instanci.

std::pair asi fungovat bude, dokud si vystačíte s jeho kopírovacím konstruktorem. Jakmile budete potřebovat přiřazení, dostanete se do problému, protože reference nejde přiřazovat.

Suma sumárum, pořád se domnívám, že nejlepší obecné řešení, pokud nenalezený objekt není chyba, je vrátit ukazatel (normální nebo smart, pokud by se předávalo vlastnictví). Pokud je chyba, vracet referenci a vyhazovat výjimku.

Nahlásit jako SPAM
IP: 94.142.234.–
Kit+11
Guru
16. 9. 2016   #20
-
0
-

#15 Staon

  1. OK
  2. NullObject sice může mít víc instancí, ale kdo potřebuje tisíce shodných objektů, když může být jeden?
  3. Třídy dělám obvykle na max. 65 řádek. Řádky nepočítám, ale vychází to tak. Pokud je ve třídě příliš mnoho metod, svědčí to spíš o porušení SRP.
  4. Výjimky a NullObject nekombinuji. Tady jsme se nepochopili. Psal jsem, že NullObject se dá využít k zotavení výjimky.

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.
Zjistit počet nových příspěvků

Přidej příspěvek

×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, 71 hostů

Moderátoři diskuze

 

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