Správa paměti v C++

11. 4. 2012 17:08 (aktualizováno) zboj

Na diskusních fórech se stále dokola opakuje tvrzení, že v C++ hrozí úniky paměti, že je na ně náchylné, případně že je pro programátora náročné psát kód bez úniků paměti a že právě proto je psaní kódu v C++ méně efektivní. Kdo toto tvrdí, je veleosel.

Sám tvůrce C++ Bjarne Stroustrup vysvětluje, jak správa paměti vzhledem k objektům funguje, nemá cenu vše opakovat. Stručně shrnuto, díky RAII se paměť uvolňuje automaticky, jakmile se proměnná dostane mimo lexikální rozsah platnosti, čímž je zajištěna korektkní správa paměti i v případě výjimek nebo jiného typu opuštění funkce někde zprostředka. Pokud se instance objektu nepředává referencí, typicky se použije move konstruktor (jeho použití si lze vynutit v případech, kdy by ho překladač nepoužil explicitně). Celá STL tyto konstruktury používá. Kdo se chce vyhnout move sémantice úplně, použije shared_ptr (nebo nějaký jiný xxx_ptr podle toho, co vlastně potřebuje) a k tomu make_shared, čímž opět přenechá správu paměti překladači a STL.

Výhoda GC spočívá v deterministické alokaci (v C++ můžete mít svůj alokátor, ale ten standardní deterministický není). Platíte za to ovšem nedeterministickým uvolňováním paměti (a hlavně vyšším footprintem). Opět lze namítnout, že existují deterministické GC, jenže ty se používají jen ve speciálních případech (typicky real-time systémech, i když tam se většinou GC nepoužívá vůbec), neboť mají spoustu jiných nevýhod.

Kdo stále ještě nechápe, nechť se prosím nevěnuje programování.

P.S. Některá rozšíření C(++) jdou ještě dále, co se týká činnosti překladače. V Objective-C(++) se díky ARC nemusíte starat o uvolňování, prostě jen alokujete. Navíc překladač inteligentně vyhazuje instrukce pro aktualizace čítače referencí všude tam, kde nejsou potřeba, a takových případů je většina (pokud to řeší knihovna bez podpory překladače, jako např. STL, není taková optimalizace možná). Velice podobnou optimalizaci ma C++/CX ve Windows 8, také jen alokujete (pomocí ref new) a instrukce pro správu čítače referencí generuje překladač jen tam, kde jsou nezbytné.

P.P.S. Je vhodné doplnit (v reakci na diskusi), že použití operátoru new se lze prakticky vždy vyhnout. Je-li z nějakého důvodu žádoucí použít explicitní ukazatel, nahradí jej make_shared. Jedinou výjimkou, která mě napadá, je použití nativní třídy v řizené třídě v C++/CLI. Tam překladač vyžaduje raw pointer. K uvolnění paměti se na první pohled nabízí metoda !Trida (finalizér). Správné je ovšem použití řízeného destruktoru ~Trida.

Sdílet