Diskuze: velikost třídy
V předchozím kvízu, Online test znalostí C++, jsme si ověřili nabyté zkušenosti z kurzu.
Člen
Zobrazeno 22 zpráv z 22.
//= 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.
k zjištění velikosti můžeš použít operátor sizeof
Přesouvat třídy pomocí memmove není zrovna nejdoporučovanější řešení, zejména když obsahují ukazatele. Pokud chceš do proměnné b dostat kopii třídy z proměnné a (obě třeba typu C *), tak v C++ k tomuto účelu slouží kopírovací konstruktor:
b = new C(*a);
Samozřejmě, pokud přesunutí v paměti pro danou třídu znamená více než kopírování, musíš kopírovací konstruktor předefinovat.
No vždycky jsem se kopírovacím konstruktorům snažil vyhýbat velkým
obloukem, ale teď mi asi nic jinýho nezbyde. memmove byl blbost, tam se ty
ukazatele nepoužívají. Tak aspoň něco.
Ale baví mě sledovat jak v odpovědích maji rádcové každý jiný názor a
styl programování, většinou bohužel - víc jak půlka píšou špatné,
nebo neůplné odpovědi.
Ď
Kopírující konstruktor není zase taková věda, jsem si docela jistej, že jamile mu přijdeš na chuť budeš ho používat i častěji. Je to docela šikovná věc.
A když používáš třeba clone(), tak je to v podstatě to samé jako kopírák ne?
Podle názvu to asi bude pravda, ale já jsem žádný clone() nikdy nepoužil, ani na cplusplus stránkách jsem to nikde nenašel
To je metoda objektu, která vytvoří jeho klon, tedy dokonalou kopii, používá se to tehdy, pokud návrh neumožňuje jiné řešení, než poslat někam klon objektu a původní objekt nechat tam kde byl, nebo ho zničit. Třeba kvůli bezpečnosti a kontrole výskytu těchto objektů v programu.
Ano, přesně jako kopírovací konstruktor, akorád mě bylo divný, že píšeš clone() jako funkci
To není kopírující konstruktor, to je metoda clone(), konstruktor zavoláš jen tak v prostoru, stejně tak ten kopírující, kdežto clone() se volá, musí volat, na již existující instanci. Třeba takto
Human clovek = new Human("Franta", 23); // beznej konstruktor
Human druhejClovek = new Human(); // defaultni
Human tretiClovek = new Human(clovek); // kopirak
nejakaFunkce(tretiClovek.clone()); // clone() udela kopii tretiClovek a vrati referenci na novej objekt, tedy nejakaFunkce dostava klona od tretiClovek a ne jeho referenci
jo takhle, to vyzkoušim, akorád ještě řešim co když budu mít v objektu nějaký ukazatel, ten se nemůže zkopírovat, ale vytvořit novej, to by tim asi nešlo že jo.
Pokud nepotřebuješ ten objekt kopírovat nějak speciálně (tzn. v jeho atributech nejsou čisté ukazatele např.), myslím, že nemusíš kopírovací konstruktor vůbec psát, protože toto obstará jeho výchozí verze (ta prostě kopírovacím konstruktorem okopíruje všechny atributy).
Co se týče memmove, pro kopírování se vyplatí použít jen v případě, že se zdrojový a cílový buffer překrývají (funkce se chová tak, jako by tam byl ještě dočasný buffer, přes nejž kopírování probíhá, takže překrývání není problém). Pokud víš, že se zdrojový a cílový buffer nepřekrývají, tak použij memcpy.
To není kopírující konstruktor, to je metoda clone(), konstruktor zavoláš jen tak v prostoru, stejně tak ten kopírující, kdežto clone() se volá, musí volat, na již existující instanci. Třeba takto
Tak, on to je spíš takový syntaktický detail. Metodu clone voláš na instanci, což znamená, že ona vlastně dostane tu instanci jako parametr (jistě, není to přímo vidět, ale implementace je taková). Kopírující konstruktor si danou instanci bere jako explicitní parametr.
Takže není problém napsat:
nejakaFunkce(new Human(tretiClovek));
Spíš je to o tom, že v některých jazycích (C++) kopírovací konstruktory máš, ale ne metodu clone a v nějterkých jazycích zase metodu clone máš (Java), ale nemáš kopírovací konstruktor.
ještě řešim co když budu mít v objektu nějaký ukazatel, ten se nemůže zkopírovat, ale vytvořit novej, to by tim asi nešlo že jo.
To záleží, zda-li si tu věc, na kterou ten ukazatel vede, přeješ zkopírovat či nikoliv. Pokud ano, tak v rámci kopírovacího konstruktoru či metody clone musíš ono kopírování pořešit (pokud to je ukazatel na třídu, stačí na ní zavolat kopírovací konstruktor resp. metodu clone a může být hotovo).
Jde o tohle, pokud si přeješ aby se při kopírování objektu nějaká jeho property (kdyby třeba v sobě mel objekt pole) zkopírovala natvrdo, nikoliv jenom reference, tak si musíš napsat kopírující konstruktor sám, v něma alokovat místo pro nové pole a přetahat obsah.
Jakože mám clovek1 = {skills: ["C++", "JS", "JAVA"]};
když teď pošlu clovek1 do kopirujícího konstruktoru, a výsledek uložím jako clovek2, tedy
clovek2 = new Human(clovek1);
tak mam pro pole skills mělkou kopii, jakmile začnu upravovat pro clovek2 jeho skilly, upravuju je i pro clovek1. Tohle nutně ne vždy chceme, takže pokud nechceme, napíšeme si vlastní kopírující konstruktor.
To jestli se provede mělká nebo hluboká kopie by default je docela zásádní a když si člověk není jistej, měl by to pokrýt nějakým experimentem.
Samozřejmě, můžeš je nahrazovat, dokud nepotřebuješ implementovat program tak, že není možné poslat stávající objekt, není možné vytvořit kopírákem novej a je nutné použít clone(), který si sám napíšeš pro svou classu, ono takhle to zní strašně umělě, ale já to zmiňuju kvůli jednomu zajímavýmu případu co jsem jednou viděl na přednášce, kde se tímhle hlídala unikátnost objektů (implementace zoologické zahrady) prostě nešlo vytvořit novýho lva, nešlo starýho poslat jen tak do novýho pavilonu, protože by programátor mohl nechat původní instanci v původním pavilonu a Lev se nám už buněčně dělí jak bakterie, tak tam byla metoda pro přesun zvířete z pavilonu, v jednom ho to zničilo v druhém vyplivnulo, takovej teleport. Netvrdím, že to byl nejlepší příklad, no prostě klasickej příklad na pochopění na vejšce.
K tomuto účelu by asi sloužil move assignment/construtor (C++11), který má navíc tu výhodu, že neprovádí vlatně kopírování. Ale už hodně záleží, jak ten příklad byl udělán, je možné, že by se musel celý přepsat.
Samozřejmě se dá i kokpírák napsat tak, aby nevytvořil novou instanci, pokud se to nesmí.
Podle mě pak už hodně záleží na tom, zda takové řešení bude pěkné/čitelné, nebo zda bude vidět, jak moc se na to člověk snažil napasovat dané konstrukty.
Děkuji všem za vyčerpávající info, jen po jistotu bych chtěl upřesnit ( jestli jsme to dobře pochopil), že teda memmove se používá v poli, kvůli tomu překrývání, a memcpy se používá když mám ukazatele zvlášť oddělené, takže vlastně v tom mým prvním dotazu nešlo použít memmove, ale právě memcpy
Děkuji všem za vyčerpávající info, jen po jistotu bych chtěl upřesnit ( jestli jsme to dobře pochopil), že teda memmove se používá v poli, kvůli tomu překrývání, a memcpy se používá když mám ukazatele zvlášť oddělené, takže vlastně v tom mým prvním dotazu nešlo použít memmove, ale právě memcpy
V C/C++ je pole implementováno jako ukazatel. Tzn. tyto dva zápisy jsou "v podstatě" ekvivalentní:
int a[25];
int *a;
Rozdíl je v tom, že v prvním případě překladač ví, že se jedná o pole o délce 25 a že tento prostor alokuje a nasměruje na něj proměnnou a, kdežto v druhém případě se jedná o ukazatel, který musíš naplnit něčím užiečným, než jej legálně použiješ. V obou případech ale můžeš napsat:
a[10] = 256;
Takže to, zda použiješ memcpy či memmove není závislé na tom, zda se jedná či nejedná o pole. Prostě pokud víš, že se zdroj a cíl nepřekrývá, použij memcpy, jinak **memmove. Ani pole se nemusejí překrývat. Obecně se dá říci, že se zdroj a cíl překrývají jen velmi zřídka.
Já teda vůbec nechci mít poslední slovo nebo tak něco, ale když pro zjednodušení říkáš nováčkovi že pole je v podstatě ukazatel, bylo by dobrý jim říct, že to není docela pravda, aby se na to dívali takhle, dokud to nechápu, ale pak, že to chce hlubší pochopení. Vím že jsi to napsal do uvozovek, ale on to z toho každý nepochopí, jinak naprosto respektuji, že to pro zjednodušení takhle uvádíš.
Zobrazeno 22 zpráv z 22.