\


 Wednesday, 06 August 2008
Recenze: Effective C++ 55 Specific Ways to Improve Your Programs

Effective C  Při nákupu na Amazonu jsem neodolal a do košíku jsem vložil už třetí edici netradičně psané knihy  Effective C++ od Scotta Meyerse. Konvenčnímu stylu průměrných odborných knížek se Effective C++ se vymyká už jen tím, že nejde o další popularizující a tisíckrát opakovanou snůšku jednoduchých rad a receptů psanou za účelem "za 21 dní či hodin uděláme z naprostého technického idiota sebevědomého technického idiota, jehož mozek dokáže na pohovoru nějakému jinému (bez) mozku z personální agentury úspěšně předstírat znalost nějaké technologie". Scott Meyers se nesnaží ani předstírat, že se z jeho knihy naučíte základy C++, ale předpokládá, že jste schopni v C++ programovat a že jste si již stačili uvědomit  temná zákoutí  jazyka C++, nebo jste v nich v horším případě na některém projektu začali i bloudit. Kniha Effective C++ je pobídkou, abyste si osvojili 55 postupů z různých výrazových prostředků jazyka C++, které vám umožní temná zákoutí jazyka C++ obcházet. Pro vývojáře, jejichž "mateřským" jazykem je C# nebo JAVA, může být zajímavé, že ve třetí edici Scott asi na základě svých špatných zkušeností na vícero místech v knize opakuje: "Takto je to možná v Javě, možná vám to nevadí ani v C#, ale zapamatujte si, že C++ není JAVA ani C#." Pořád lepší být mentorován a poučován od člověka, který svoje varování vyřkne proto, že se pravděpodobně při svých konzultacích setkal s odstrašujícími ukázkami kódu, který byl zcela bezmyšlenkovitě převeden ze C# do C++, než být vlečen projektovým osudem vtěleným do Ganttových diagramů , který umí být mnohem nemilosrdnější než běžný lidský osud a donutí ve svých dosavadních garbage collectorem stvrzených jistotách spokojeně si hovící vývojáře zabývat se C++ bez možnosti utéct zpět do závětří "managed" kódu. :-)

V úvodu Scott popíše, jaké termíny a pojmy by měl každý vývojář v C++ znát a hned v prvním článku popíše své vidění jazyka C++. Jazyk C++ je pro něj spíše federaci poměrně samostatných jazyků, než jedním klasicky strukturovaným programovacím jaykem. C++ vám dovoluje psát programy v klasickém "C" stylu, můžete pracovat s třídami, dědičností a virtuálními metodami stejně jako v každém jiném jazyce podporujícím OOP. Také ale se můžete nechat zlákat šablonami a metaprogramováním a využít toho, že na první pohled ničím nevynikající a dříve některými jen za chytřejší preprocesor  považované šablony jsou "turingovsky" úplným jazykem. V knize je za autonomní jazyk považována i knihovna STL, i když autor vysvětluje, že za dalšího člena C++ federace ji považuje hlavně proto, že přináší další programové konvence a idiomy (funktory, "chytré" pointery", iterátory, ...), které musíte bezpodminečně ovládat , chcete-li použivat další knihovny třetích stran, jež jsou na těchto standardních technikách a postupech často založeny.

V dalších článcích se Scott zabývá nutností inicializovat všechny proměnné. Při zdůrazňování důležitosti konstantních členů třídy pookřejete, že konečně nějaký autor pořádně vysvětlil, jak správně použít klíčové slova mutable, ale možná nebudete spokojeni s motivací pro dvojí přetypovávání (const_cast) u konstatních metod třídy (například indexer - operator[]), které se odkazují na své nekonstantní protějšky, protože v nich nechcete duplikovat kód.

Dalším velkým tématem jsou konstruktory, destruktory a operátory přiřazení. V článcích se dozvíte, jak neopakovat typické chyby při psaní kopírovacích konstruktorů nebo operátorů přiřazení, jak zajistit, že při vytváření hluboké kopie objektu inicializujete i zděděné členy, nebo proč je pro vaši aplikaci smrtící, když z destruktoru objektu necháte vybublat výjimku. I u těchto nepříliš zajímavých témat oceníte Scottův živý a vášní pro své téma prodchnutý jazyk. Když upozorňuje na to, v jakých případech vývojáře kompilátor nebude nijak varovat, že kopírovací konstruktory a operátory přiřazení nefungují správně, sarkasticky k tomu dodá, že jde pravděpodobně o odplatu za to, že už nenecháte kompilátor, aby si sám vyhrál s deklarací kopírovacích konstruktorů a operátorů a přiřazení. Když popisuje, jak sdílet kód mezi vícero konstruktory, místo zabíhání do nesmyslných podrobností debatu rázně utne tvrzením, že se sice můžete zkusit odkázat z jednoho konstruktoru na jiný konstruktor a že na první pohled mohou vaše konstrukce vypadat pěkně, ale že stejně jde o nesmysl a jazyk C++ nic takového nedovoluje. Chcete-li sdílet kód v konstruktorech, dostanete praktickou radu, ke které byste asi sami stejně dospěli, abyste společný kód vyčlenili do samostatné privátní "init" metody. 

V dalších částech se Scott zabývá chytrými pointery. Celá kapitola 4 s články 18 až 25 je vyhrazena návrhu správného rozhraní třídy. Principy jako "navrhujte rozhraní tak, aby jej bylo jednoduché použít, ale velmi komplikované zneužít" asi v nikom nevyvolají nevoli nebo chuť polemizovat. S článkem preferujte nečlenské a non-přátelské (non-friend) funkce nad členskými už asi ale každý nebude souhlasit, i když Scottovy argumenty nejsou špatné. Mně je ale bližší C# a další jazyky, které vás nutí umístit každou funkci do těla třídy. V C++  uplatňuji pouze příslovečnou výjimku z pravidla u operátorů a funkcí, u nichž vyžaduji, aby k implicitní konverzi argumentů mohlo dojit u všech argumentů, což je téma, které i Scott pěkně popisuje v článku 25 nazvaném "Declare non member functions when type conversions should apply to all parameters".

Slyšeli-li jste někdy letmo o PIMPL idiomu a divili jste se, k čemu by vůbec byl dobrý, nebo vám vadila u projektu dlouhotrvající a nutná rekompilace po změně jednoho hlavičkového souboru z nějaké knihovny? V knize se dozvíte, jak "kompilační" závislosti omezit právě s využitím do všech podrobností dokonale vysvětleného PIMPL postupu. Kdo je stále zajatcem bludu, že dědičnost a virtuální metody jsou jediným všemocným kladivem na všechny problémy, měl by si pořádně pročíst kapitolu zabývající se dědičností, ve které Scott zmiňuje, jak poznat a implementovat vztah "je podtypem" (veřejná dědičnost"), nebo "využiji nějaké služby jiné třídy" (kompozice, privátní dědičnost). Také fandové vícenásobné dědičnosti by si měli podrobně přečíst, proč Scott její použití nijak nezakazuje, ale jemně v názvu článku 40 varuje, že by měli být soudní. Protože jinak při šíleném křížení tříd můžete velmi rychle  o rozum a logický úsudek přijít, dodal bych já. A možná rádi, abyste nemohli už dnes přesně usuzovat, kolik ještě stačíte na dohodnuté podpoře aplikace za několik let prodělat. :)

Šablony jsou tématem kapitoly 7, ale výklad není veden příliš do hloubky. Scott se spíše soustřeďuje na řešení klasických "špeků", jimž se při použití šablon nevyhnul asi nikdo. Podědili jste z šablonové třídy, voláte metodu z bázové třídy a kompilátor protestuje, Scott ukazuje, jak "chybu" odstranit - ať už použitím klíčového slova this (this->MetodaZBazoveTridy();), instruováním překladače o existenci metody v bázové třídě za pomoci klíčového slova using (using BazovaTrida<Typ>::MojeMetoda;) nebo uvedením jména bázové třídy před jménem metody při volání (BazovaTrida<Typ>::MojeMetoda();). Sympatické je, že nezůstane u prostého výčtu možných řešení problému, ale Scott upozorní, v čem tkví nedostatky jednotlivých řešení, takže u poslední varianty se vám ihned dostane varování, že jde o nejméně vhodné řešení, protože bude-li metoda MojeMetoda virtuální, tak jste se explicitním odkazem na implementaci metody v bázové třídě o virtuální chování mnohdy nechtěně a navíc hloupě připravili. Pěkný je i článek 45, který dostačujícím a přehledným způsobem popíše význam šablonových funkcí (nejen) na příkladu užitečného zobecněného kopírovacího konstruktoru se šablonou a nechybí  v něm ani vysvětlení, proč v C++ zobecněný a na šablonách založený konstruktor nikdy nenahradí běžný nešablonový konstruktor.  Důležitá je i kapitola, která shrnuje, proč byste měli nečlenské funkce definovat přímo v šablonové třídě, jestliže požadujete, aby všechny argumenty metody podporovaly automatické konverze. Opomenut není ani význam rysů (traits) u šablonového programování.

V další kapitole Scott zmíní, kdy je vhodné nahradit standardní operátory new a delete a také, jak nejlépe vyhovět všem konvencím při nahrazování operátorů new a delete. (článek 52 pojednává o nutnosti poskytnout "placement" operator delete, jestliže jsme se rozhodli používat vlastní placement "new" operator)

V poslední kapitole s radami, které se nevešly jinam, kniha dává na pranýř ty programátory, kteří si myslí, že varování kompilátoru nejsou přece žádnou chybou a že tedy na ně nemusejí brát ohledy, protože by jim zkazila nadšení, že tu svou nádhernou sbírku chybových bajtů zkompilovali, nebo protože si už vypěstovali vůči varovným hláškám selektivní slepotu. Poslední dva články vyzývají vývojáře, aby si osvojili algoritmy, kontejnery, iterátory a postupy využité v STL a v studnici nápadů zvané BOOST.

Líbí  se mi, že na konci každého článku je rychlé shrnutí, co byste si měli určitě zapamatovat.

Ani kniha Effective C++ není dokonalá a najdeme v ní mnoho kapitol, u nichž si zkušenější vývojář řekne, že existují i pokročilejší postupy. Například v článcích o objektech a šablonách by se slušelo zmínit, jak pomocí částečné specializace můžeme odstraňovat/přidávat modifikátory const.

template<class T>struct ConstHelper

typedef const T Add;

typedef T Remove;  

typedef const T Invert;  

 

 

};

template<class T>struct ConstHelper<const T>

{  

typedef const T Add;

typedef T Remove;  

typedef T Invert;  

 

};

 

typedef typename ConstObject<T>::Add const_parameter;

 

Šablony a jejich využití při metaprogramování je vměstnáno do jednoho přehledového článku ("článek 48 - Be Aware of template metaprogramming). Výjimky jsou zmíněny jen okrajově. Tyto výtky ale platí jen zčásti, protože Scott napsal druhý díl nazvaný More Effective C++ a dluh vůči knihovně STL splatil v další knize Effective STL.

Scottovi se podařilo napsat čtivou a přitom po odborné stránce nijak neošizenou knihu, jejíž brilantní formulace i postřehy jsou projevem zajímavé osobnosti, která netrpí tou příšernou a nudnou falešnou skromností, která alespoň mě vadí u jiných autorů, okázale zdůrazňujících ve svém díle  všechny spolupracovníky, známé, přicmrdávače i vtěrky, aby za každou cenu dali najevo, jak oceňují práci druhých a jak jsou jen malým mravenečkem při stavbě velkého díla, jímž je jejich kniha. Tento druh povinné a společensky přívětivě přijímané falešné skromnosti mi totiž přijde jen nepříliš rafinovanou zástěrkou pro skutečnou a nelichotivou skromnost duševní (snad jen té :-) ) potence a navýsost egoistickou snahou možný publikační neúspěch (pod) průměrného, frázovitého, a dokonce mnohdy i po té, tolikrát zdůrazňované jako jediné významné či rozhodující, odborné stránce nepřesného díla omluvit a zdůvodnit slovy: "Podívejte se, že za ten paskvil opravdu nemohu sám". Bohužel ani kvantitativně početné stádo nevyvolených duševně chudých nemůže supeřit s jedním Scottem Meyersem. Scott si nebere žádné servítky a jak se dá z jeho knihy vytušit, nečiní mu žádný problém v jím (spolu)pořádaných anketách nominovat a jistě po těžké úvaze :-) i zvolit na čelní místa (a já dodávám zcela oprávněně) sebe a své knihy. A svým kritikům, kteří na něho vytahují, že není žádný programátor s rozsáhlou praxí, s klidem výstižně odpovídá, že on strávil mnoho času studiem jazyka a všech jeho rysů včetně slepých uliček právě proto, aby ostatním pomohl v jejich praktickém programování.  Teoretický i praktický rozum se doplňují a nekonkurují si. Je tristní pohled na vývojáře, který si osvojil 10% syntaktických i sémantických konstrukcí a knihoven z  nepřeberného bohatství každého programovacího jazyka a pak své praktické "informační systémy" plácá s pomocí jedné bábovičky. Osobně tomu říkám syndrom "všechno vyřeší volání grid.DataBind()", což ale je srozumitelné asi jen ASP.NET vývojářům, ale podobné typy vývojářů(?) se najdou všude.

Motem knihy je citát z Petroniova Satyriconu: "Wisdom and Beauty form a very rare combination".  Této knize se to podařilo.



Wednesday, 06 August 2008 18:29:08 (Central Europe Standard Time, UTC+01:00)       
Comments [2]  Ostatní