Diskuze: Problémy s pointery
V předchozím kvízu, Online test znalostí C++, jsme si ověřili nabyté zkušenosti z kurzu.

Tvůrce

Zobrazeno 50 zpráv z 50.
//= Settings::TRACKING_CODE_B ?> //= Settings::TRACKING_CODE ?>
V předchozím kvízu, Online test znalostí C++, jsme si ověřili nabyté zkušenosti z kurzu.
S tím nesouhlasím, existuje určitá pravděpodobnost, že programátor udělá chybu. Programátor je totiž člověk. Ta se samozřejmě zvyšuje s náročností projektu. Záleží na jazyku, jak se tato chyba projeví, managed jazyk na ni upozorní, unmanaged ne a hledá se to dost blbě, vesměs metodou pokus/omyl.
Já neprogramuju v ničem jinym než v C++, zkoušel sem i jiný jazyky ale žádnej mi nevyhovoval... nevim jak v klasickym C, protože v něm sem nikdy nic většího nepsal ale v C++ sem se správou paměti nikdy neměl moc problém. Taková chyba ve správě paměti aby ti kvuli tomu program padal se ti nestane pokud si dáváš pozor a když se stane, najít jí většinou neni zase takovej problém... Může se stát že někde zapomeneš dealokovat pár bytů ale garbage collector taky v paměti nějaký místo zabírat musí takže myslim že je jedno jestli zapomeneš pár bytů sám nebo ti pár bytu v paměti zabírá GC... nehledě na to že v C++ si na všechny důležitý věci který potřebujou dynamickou paměť můžeš napsat knihovnu ve který to vyřešíš, otestuješ a dál se u tý věci o správu paměti nemusíš starat. Třeba na dynamický pole si snadno uděláš šablonu třídy, pak jenom vytvoříš objekt, nastavíš datovej typ a velikost a o alokaci/dealokaci paměti se postará konstruktor/destruktor... stačí si umět poradit a vážně je to o zvyku...
Alokaci paměti může dělat konstruktor, ale dealokace není práce destruktoru, ale až garbage collectoru. Destruktor pouze ukončí platnost objektu, ale o fyzickou dealokaci se nestará.
C++ GC nemá takže tam to dělá z pravidla destruktor. Pokud teda nechceš po skončení platnosti objektu nechat paměť z nějakýho důvodu alokovanou.
To souhlasí. Bohužel tohle nepříliš logické spojení bylo převzato i do C#, takže destruktor je tam spouštěn až garbage collectorem, což je někdy bohužel pozdě. Proto vývojáři C# přidali berličku "final", která tuto chybu obchází.
Ohledně C++ mi to logický přijde, konstruktor v době vzniku objektu alokuje paměť a destruktor ji v době skončení platnosti objektu dealokuje. Jak je to v C# nevim takže nemůžu soudit.
Jsou to 2 nezávislé akce. Jedna je zneplatnění objektu, druhá je fyzické odstranění z paměti. V C++ můžeš udělat jednu nebo druhou operaci - nejčastěji obě.
Podobně je to u lepších souborových systémů. Příkaz ke smazání souboru pouze smaže jméno souboru, ale fyzicky je soubor zlikvidován, až jsou smazány všechny jeho názvy a až ho zavřou všechny procesy, které s ním pracují. To umožňuje mazat otevřené soubory.
Tak jsem to myslel. V C++ se konstruktor volá vždy v době vzniku a
destruktor vždy v době skončení platnosti. Jestli v konstruktoru alokuješ
paměť a v destruktoru dealkuješ záleží na tobě, ale většinou se to tak
dělá.
Operátor delete třeba zničí objekt a způsobí zavolání jeho destruktoru
ale destruktor nemusí dealokovávat veškerou paměť kterou objekt používal.
Jinak pokud objekt nebyl vytvořen operátorem new tak jeho platnost končí s
blokem ve kterém byl deklarován a v tu chvíli se volá destruktor.
Opět jsi napsal něco jako když si dáváš pozor, tak se to nestane. To je ale špatný přístup, protože člověk není stroj a tohle je práce pro stroj. Viděl jsem několikrát co je to schopno udělat, takové bugy se průměrně hledají asi 2 dny, to je přeci jen dost práce. Je to proto, že chyba se projeví jen někdy (záleží na obsahu neuvolněné paměti) a v jiných částech aplikace, než kde je chyba. Kolega v objective C hledal podobnou botu 2 dny (naštěstí v novějších verzích už Apple přidal GC), i já jsem si v Delphi užil s tímhle své (také řádově dny). Že tě to ještě nepotkalo neznamená, že se to neděje.
S tim souhlasim, proto ale tvrdim že je to individuální, někdo si pozor
dává a třeba se mu to nestává. A nebo jak sem psal, udělat si knihovnu,
tak se to v C++ řeší asi nejlíp, samozřejmě že je trochu otrava u
velkýho programu neustále kontrolovat kde dealokovat který pole, pak neni nic
jedoduššího než si napsat třídu pro pole, alokaci/dealokaci vyřešit v
konstruktoru/destruktoru a dál se nemusíš starat o nic, jenom používat...
nebudu přece používat jiný jazyk jenom proto že má automatickou správu
paměti když mi jinak ten jazyk nevyhovuje a se správou paměti většinou
nemám problémy.
Musím se taky přidat k názoru, že dělat chyby je lidské. Dokonce i v programovacích jazycích vyšší úrovně, kde je GC a spoustu dalších vymožeností, nejsou programy a hlavně programátoři bezchybní. Proto se taky píší testy, ale to je zase jiné téma. To, že ti to nedělá problém, je dost silné tvrzení. Pokud budeš dělat větší aplikaci, dovolím si tvrdit, že uděláš chybu na 100%. Je pak na tobě, za jak dlouho si jí všimneš (jestli vůbec), za jak dlouho ji najdeš (což může být opět velmi těžké) a jak se ti ji povede opravit.
Tak samozřejmě, chyby prostě vznikají, netvrdim že se mi nikdy nestala
chyba ve správě paměti ale mnohem horší chyby se mi stávali u uplně
jiných věcí a jejich nalezení a odstranění mi trvalo daleko delší dobu.
...a jak už sem řikal,
když si na často používaný věci napíšeš knihovny a napíšeš je
dobře, tak se kolikrát o správu paměti ani nemusíš starat. Třeba v
klasickym C kde nejsou třídy bych s tim asi měl větší problémy, proto
programuju v C++. Nejčastější věc kde používáš dynamickou správu jsou
pole, a na to se dá třída napsat celkem snadno...
Ale ne, vůbec to není jen záležitost polí, stačí zapomenout, že jsi už uvolnil pointer na objekt. Měl by sis o tom něco přečíst.
Vzpomněl jsem si, že když psal strejda interpreter vlastního jazyka v C++, hledal dlouho jeden leak. Problém byl, že mu ta chyba narušovala debugger, takže ho nemohl používat a hlásil nesmysly.
Neříkal jsem vždy, říkal jsem nejčastěji... pointery na objekty zase
tak často nepoužívam. Nejhorší a nejčastější chyby ve správě paměti
mi vždycky vznikaly v dynamických polích (Až už to byly pole proměnných
nebo objektů - což vlastně je pointer na objekt).
Já tu netvrdim že automatická správa paměti je špatná věc, je to dobrá
věc. Snažim se říct že (alespoň pro mě) to neni ta nejpodstatnější
vlastnost pro volbu vyhovujícího programovacího jazyka. Proč programovat v
C# nebo Javě když kromě toho že mají GC mi ve většině ohledech ani jeden
z těch jazyků nevyhovuje. Když někomu jinýmu vyhovujou, tak dobře pro
něj.
Teď najednou píšeš, že ti vznikaly "Nejhorší a nejčastější chyby ve správě paměti", tvrdil jsi, že jsi s tím nikdy problém neměl, zvláštní. Možná ses jen na moderní jazyky špatně podíval, člověk se těžko vzdává něčeho, co umí a přechází na něco lepšího, co ještě neumí. Co konkrétně se ti nelíbilo?
Tu větu sis špatně přečet ...napsal sem že nejhorší a nejčastější chyby ve správě
paměti mi vznikali v polích (a to v dobách kdy sem ještě C++ pořádně
neuměl a programoval jako prase) neni tam řečeno že ty chyby byly nějak
závažný a rozhodně mi jejich oprava netrvala několik dní. Už sem tu
jednou psal že daleko horší chyby mi vznikají v uplně jiných věcech.
Věc která mi na těchhle jazycích vadí uplně nejvíc je jejich na muj vkus
přehnaná objektová orientace. Všechno musí bejt v nějaký třídě, ke
všemu musíš používat objekty, skoro všechnou jsou objekty... Mě se líbí
jak je to v C++, když třídu potřebuju tak si ji udělam, když ne tak
programuju procedurálně, C++ prostě všemu nechává větší volnost. I
způsob jakym tam fungujou právě reference se mi líbí mnohem víc.
Když bude potřeba tak nebudu mít velkej problém se třeba Javu naučit, což
stejně budu muset až pudu na VŠ. Obráceně přecházet z Javy nebo C# na
C/C++ by asi bylo horší.
Když už je řeč o refencích, mam malej dotaz. Vždycky mě zajímalo jak
se v Javě a C# řeší když potřebuju funkci jako parametr předat proměnnou
a chci aby když jí ta funkce změní aby se změnila i ta proměnná kterou
sem za ten parametr dosadil. V C++ se tohle řeší právě přes reference.
Pokud se v C# a Javě dá vytvořit reference na obyčejnou proměnnou, tak jak?
A pokud ne, tak jak se to řeší? Předem díky za odpověď?
Co je to vlastně "dynamické pole"? Obvykle se pod tímto pojmem skrývá implementace některého z abstraktních datových typů, např. seznamu, slovníku, zásobníku, fronty apod. V C#, Javě a podobných jazycích se jen vybere správná kolekce a ta se použije. Pokud to někdo šmudlí v "dynamickém poli", dobře mu tak. Pokud sis tyto abstraktní struktury odladil, pak ti mohou i dobře sloužit jako moduly, ale vytvářet je stále znovu je hloupost.
Opět jsem přesvědčen, že tyto názory jsou pouze důsledkem nevědomosti a neznalosti objektového přístupu. Míchat objektový a procedurální kód je asi to nejhorší, co můžeš udělat, to poznáš sám, až pochopíš, jak objekty fungují. Větu "Přecházet z Javy na C# by bylo horší" nechápu, protože C# JE Java (přesněji z ní vychází).
Tvůj dotaz je špatně položený, protože nevíš, jak OOP funguje. Sice
to C# umožňuje modifikátorem u parametru a Java určitě také, ale ve
správně napsaném programu to nebudeš potřebovat. Ty sis jen otevřel Javu a
zkoušel v ní programovat procedurálně, ani se nedivím, že ti to moc nešlo
Reference se v Javě a C# nedělají, protože je to prasárna. I když se to dá snadno obejít, když si tu proměnnou nadefinuješ jako objekt.
Aby to nepochopil špatně, že se pro každou proměnnou, co chce předat,
musí dělat objekt Tady je
otázka proč vůbec modifikovat nějakou proměnnou. Ta proměnná něčemu
patří, má nějaký kontext, nějaký objekt. Proto předám ten objekt a
modifikuji stav tohoto objektu ať již metodou nebo přímo.
Podle popisu mi spíš připadá, že Lukáš Hruda v C++ používá objekty jen za účelem vytvoření datové struktury proměnné délky, ale jinak programuje procedurálně. V tom případě bychom mu ale nemohli vyčítat, že by kombinoval procedurální a objektové programování.
Nenapsal sem z Javy na C# ale z Javy nebo C# na C/C++ Já chápu jak fungujou objekty v
rámci C++. Ten jazyk je dělanej na to aby se v ném míchalo objektový a
procedurální programování, taky to neni čistě OOP jazyk. Java a C# jsou
čistě objektový jazyky a to je asi ten hlavní důvod proč se mi nelíbí.
Líbí se mi styl jakym je to řešený v C++, objekty tam sou a já je často
používam ale někdy mi prostě přijde lepší programovat bez nich. I v C++
můžeš programovat čistě objektově pokud chceš, ale přijde mi jako
hloupost tam dělat třídu a následě objekt kvuli každej funkci. V podstatě
se dá říct že mi nevyhovuje čistě objektový programování a proto mi
nevyhovuje Java ani C#... Rozhodně si nemyslim že C++ je zastaralej jazyk a
uplatnění si určitě najde a co se týče absence automatycký správy
paměti, tak to sme tu myslim řešili už dost.
"přijde mi jako hloupost tam dělat třídu a následě objekt kvuli každej funkci" - A to se jako někde dělá? Když chci letadlo, udělám si objekt Letadlo a dám mu atributy rychlost, počet_kormidel, jméno_kapitána, vzorek_preumatik, hladina paliva a nevím co všechno ještě. Funkcí tam dám také několik, třeba zasuň_podvozek(), přistaň(), vzlétni(), zapni_autopilota() a podobně. Jak říkám, vůbec nevíš, jak objekty fungují, tvé odpovědi nemají a nebudou mít žádný význam, dokud si o tom něco nenastuduješ.
A co když chci funkci která do žádnýho objektu prostě nepatří... třeba funkci která prohodí dvě hodnoty, nebo funkci která seřadí pole, funkci která převede číslo do jiný soustavy, jakoukoliv všeobecnou funkci u který chci aby stála sama o sobě a volala se samostatně, maximálně byla součástí jmenného prostoru, což v C++ je v podstatě statická třída. Přece kvuli tomu nebudu dělat objekt.
A co když chci funkci která do žádnýho objektu prostě nepatří... třeba funkci která prohodí dvě hodnoty, nebo funkci která seřadí pole, funkci která převede číslo do jiný soustavy, jakoukoliv všeobecnou funkci u který chci aby stála sama o sobě a volala se samostatně, maximálně byla součástí jmenného prostoru, což v C++ je v podstatě statická třída. Přece kvuli tomu nebudu dělat objekt.
Proč se to přidalo dvakrát?
Java i C# mají také statické třídy. Setřídění pole uděláš tak, že zavoláš pole.Setrid(). Ta metoda setřiď patří poli, nevím, proč by měla být někde jinde.
2x se to přidalo proto, že jsi klikl 2x na tlačítko odeslat.
Takovou funkci bys ani neměl chtít. Pokud potřebuješ prohodit 2 hodnoty v objektu, tak si na to uděláš metodu. Metoda pro řazení je již definována, převod do jiné soustavy také a vždy to patří nějakému objektu.
To byly jenom příklady, běžně potřebuju funkce který mají význam jenom v danym programu a potřebuju aby šli použí samostatně a ne s objektem. Třeba jako funkce na prohození dvou hodnot mimo objekt...
C++ statický třídy nemá a klasický pole v C++ neni objekt ale pointer, s nim těžko zavoláš metodu. Samozřejmě můžeš mít třídu jejíž objekt se chová jako pole, pak by to takhle šlo, ale myslel sem to všeobecně.
Otázkou je, jestli ty dvě hodnoty nepatří náhodou do jednoho objektu. V objektovém programování se samostatné proměnné moc nepoužívají.
To vypadá, jako kdybys chtěl implementovat nějaký algoritmus, který již je součástí objektových jazyků. Jinými slovy v OOP takovou funkci nejspíš nebudeš potřebovat.
Nevim jak to napsat aby to bylo srozumitelný. Když se podíváte do zdrojových kódů k tomu mýmu tetris tak třeba funkce delay, set_block_types, restart, set_highscore, get_highscore, line... takový funkce mam na mysli, ty se prostě nehodí dávat do jakýhokoliv objektu, prostě stojí samostatně.
Každá funkce někam patří, jen to ještě nevíš, protože nad tím
nejsi zvyklý přemýšlet
Pokud nejde mít na poli funkci, udělal bych si statickou třídu třeba
ArrayUtils a do té dal metody pro práci s polem, jistě jich bude více,
třeba hledání. K čemu používáš prohození 2 čísel? Pokud k řazení,
má být také ve třídě ArrayUtils. Ještě se mi nestalo, že bych si dělal
třídu pro 1 funkci, udělám si tolik tříd, s kolika objekty pracuji a
funkce si přehledně rozdělím tam, kam patří. I před OOP se přeci funkce
členily do nějakých hlavičkových knihoven, objekty na to jen navazují a
umožňují ještě přehlednější kód.
Nic nikdy nestojí samostaně. Jsou to funkce hry, patří do objektu Hra,
Level, Tetris, pojmenuj si to jak chceš Pokud se ti stane, že je pro nějakou část hry moc funkci (třeba
pro kostku), je dobrý nápad je vyčlenit do objektu kostka (rotace, pád,
generování nové, načtení vzorů ze souboru...)
Právě tyto funkce by měly být součástí objektů, protože vůbec nejsou samostatné. Patří ke svým datovým strukturám.
Jelikož C++ nemá statický třídy tak pokud funkce nemá fungovat s objektem tak mi přijde jako hloupost dělat třídu plnou statických funkcí když ty funkce můžu napsat samostatně, bude to kratší a chovat se to bude stejně. Nebo dělat třídu kvuli jednomu objektu a pak volat funkce jako metody toho objektu. Přijde mi jednodušší ty funkce deklarova samostatně, často se používají jmenné prostory ale ty se většinou pak stejně obchází direktivou using...
Třída plná statických funkcí je statická třída, i když to jazyk nepodporuje, tak takto je definovaná, tedy C++ je evidentně má. Samostatná funkce je jen historický přežitek, stejně jako třeba globální proměnná.
Fajn, chápu ale řekni mi... když teď vezmu všechny ty samostatný funkce, vytvořim tedy statickou třídu třeba Game a všechny ty funkce a globální proměnný do ní dam... čim si vlastně pomůžu? Akorat když cokoliv stoho budu chtít použit tak to budu muset použít s tou třídou takže jenom místo restart(); budu volat Game::restart(); ...nevidim zásadní rozdíl, možná v určitých případech by to bylo přehlednější ale v menších projektech je to řek bych celkem jedno a způsob používání se nezmění vůbec.
Mě třeba v C# vždycky přišlo jako otrava psát třeba Console.WriteLine(...) ...místo jednoduše napsat jenom WriteLine(...) já vim že sou to maličkosti ale mě to prostě vadí.
Neobjektové části kódu se v objektových jazycích vždycky dělají trochu hůř. Proto je lepší v nich psát objektově.
To ti vadí jen proto, že máš špatný návyk ze zastaralých jazyků. Když píšu do konzole, píšu Console.WriteLine, když píšu do souboru, píšu soubor.WriteLine, když píšu řádku kamkoli jinak, opět to budu volat na tom objektu. Kam dospěje jazyk, kde jsou funkce samostatně, se můžeš podívat u PHP, není to hezký pohled. Stejně má třeba všechny funkce pro pole pojmenované jako arrayněco, to už není rozdíl v tom, jestli to máš ve třídě.
Nedělal bys to jako statickou třídu, ale jako normální třídu. Tady je problém v tom, že ty OOP vůbec nerozumíš a myslíš si, že OOP je jen to, že vložíš metody do třídy a pak musíš psát delší kód. Jinými slovy nevidíš výhody (dědičnost, zapouzdření, polymorfismus...) a vidíš jen nevýhody (kterých je jen zlomek). Pouštět se tu do vysvětlování OOP je asi zbytečné, hlavně jsem to již dělal v článcích, přečti si toto: http://www.itnetwork.cz/…programovani a potom si stáhni a projeď zdroják tohodle: http://www.itnetwork.cz/…mi-bojovniky
Dobře tak bych místo statický třídy vytvořil normální třídu a
udělal její objekt Game a pak bych volal Game.restart() to už je celkem
jedno... žádnej další objekt tam nepotřebuju, kdyby jo tak bych tu třídu
udělal. Já třídy používam když potřebuju vytvořit takovej datovej typ
kterej obsahuje víc informací a potřebuju aby její objekty mohli
prostřednictvím čelnských funkcí vykonávat nějaký činnosti, nebo když
chci vytvořit datovej tak aby se pomocí přetížení operátrů a
konverzních funkcí choval jako základní datovej typ.
Když budu programovat RPG tak si třeba vytvořim třídu Weapon, ta bude mít
proměnný damage,accuracy,... a funkce třeba Attack, pak můžu používat
její objekty který budou třeba součástí tříy Character, atd. Dědičnost
se v tomhle případě taky dá dobře využít. Napřiklad kdybych odvodil
třídu Enemy od třídy Character. V tomhle třídy pomáhají a takhle je
používam a pokud vim tak přesně k tomuhle jsou třídy v C++ určený. Ale
nevidim důvod proč psát třídu pro jeden objekt když dobře vim že víc
objektů nikdy potřebovat nebudu...
Počkej, počkej. Jak můžeš udělat třídě objekt Game? Četl jsi, na co jsem tě navedl? Objekt je myšlený většinou ve smyslu instance, tu uděláš z třídy. Ta hra v sobě bude mít přeci další objekty, jako Kostka (ta co padá), příští kostka, dále třeba hrací plocha (to jsou ty napadané kostičky). Hra samoztná na sobě bude mít třeba metodu restart(), kostka třeba orotuj(), hracíplocha třeba připojkostku().
EDIT: Jo její objekt, no dobře, ale říkejme tomu prosím instance.
Promiň, že čtu jak ponocný, ale docela dlouho jsme se nevyspal
Ty používáš třídy jako byly ve starém céčku struktury. Třída toho ale umí mnohem víc, než jen držet data.
" nevidim důvod proč psát třídu pro jeden objekt když dobře vim že
víc objektů nikdy potřebovat nebudu" - Třeba proto, aby ten kód dával
smysl Úplně stejně bych ti
mohl říct, že nevidím důvod, proč nemít všechny proměnné globální,
že mi to tak vždycky bude fungovat.
Udělat objekt třídy Game jsem myslel jako udělat třídu a její instaci
Game...
A co se týče toho ostatního co si teď napsal, takhle přesně to v tom
programu je, ta hra v sobě má objekty jako kostka co padá, další koska,
spadlý kostičky, přednastavený typy kostek který můžou spadnout... jenom
to všechno je přímo v programu a ne v další třídě. V C++ nemáš
žádnou základní třídu a funkce main stojí samostatně, proč si to dělat
složitější tím že budu za každou cenu všechno dávat do nějaký
třídy. Tak jak ten kód je mi to smysl dává naprosto perfektně.
Špatně je dost relativní pojem, píšu to tak jak mi to vyhovuje Pokud vim tak v C++ se
kombinovanym stylem programuje běžně
Zobrazeno 50 zpráv z 50.