NOVINKA - Online rekvalifikační kurz Java programátor. Oblíbená a studenty ověřená rekvalifikace - nyní i online.
NOVINKA – Víkendový online kurz Software tester, který tě posune dál. Zjisti, jak na to!

Diskuze: velikost třídy

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

Aktivity
Avatar
petr.dar
Člen
Avatar
petr.dar:14.6.2016 4:31

Zdař všichni, při programování jsem narazil na problém:
Teoreticky: Potřebuju přesunout hodnotu ukazatele z A do B.
když napíšu B = A tak to nestačí, protože A můžu změnit, smazat v B pak nebudu mít nic.
tak to zkoušim vyřešit přes

#include <cstring>
void * memmove ( void * destination, const void * source, size_t num );

Ale jakou hodnotu mám napsat místo num, když přesouvám vlastní třídy? Jak se zjistí velikost třídy?
Ď

 
Odpovědět
14.6.2016 4:31
Avatar
martanec
Člen
Avatar
Odpovídá na petr.dar
martanec:14.6.2016 7:56

ako sucet velkosti atributov(memberov) danej triedy

 
Nahoru Odpovědět
14.6.2016 7:56
Avatar
Odpovídá na petr.dar
Luboš Běhounek Satik:14.6.2016 8:43

k zjištění velikosti můžeš použít operátor sizeof

Nahoru Odpovědět
14.6.2016 8:43
https://www.facebook.com/peasantsandcastles/
Avatar
Martin Dráb
Tvůrce
Avatar
Odpovídá na petr.dar
Martin Dráb:14.6.2016 8:49

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.

Nahoru Odpovědět
14.6.2016 8:49
2 + 2 = 5 for extremely large values of 2
Avatar
petr.dar
Člen
Avatar
Odpovídá na Martin Dráb
petr.dar:15.6.2016 15:28

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.
Ď :-)

 
Nahoru Odpovědět
15.6.2016 15:28
Avatar
Odpovídá na petr.dar
Neaktivní uživatel:15.6.2016 15:45

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.

Nahoru Odpovědět
15.6.2016 15:45
Neaktivní uživatelský účet
Avatar
petr.dar
Člen
Avatar
petr.dar:15.6.2016 15:59

Jasně, jsou dobrý možná jako konverzní funkce, ale jinak všude možně používám ukazatele a odkazy abych se jim vyhnul.

 
Nahoru Odpovědět
15.6.2016 15:59
Avatar
Odpovídá na petr.dar
Neaktivní uživatel:15.6.2016 16:52

A když používáš třeba clone(), tak je to v podstatě to samé jako kopírák ne?

Nahoru Odpovědět
15.6.2016 16:52
Neaktivní uživatelský účet
Avatar
petr.dar
Člen
Avatar
Odpovídá na Neaktivní uživatel
petr.dar:15.6.2016 16:59

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 :-D

 
Nahoru Odpovědět
15.6.2016 16:59
Avatar
Odpovídá na petr.dar
Neaktivní uživatel:15.6.2016 17:31

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.

Nahoru Odpovědět
15.6.2016 17:31
Neaktivní uživatelský účet
Avatar
petr.dar
Člen
Avatar
Odpovídá na Neaktivní uživatel
petr.dar:15.6.2016 17:46

Ano, přesně jako kopírovací konstruktor, akorád mě bylo divný, že píšeš clone() jako funkci

 
Nahoru Odpovědět
15.6.2016 17:46
Avatar
Odpovídá na petr.dar
Neaktivní uživatel:15.6.2016 17:53

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
Nahoru Odpovědět
15.6.2016 17:53
Neaktivní uživatelský účet
Avatar
petr.dar
Člen
Avatar
Odpovídá na Neaktivní uživatel
petr.dar:15.6.2016 18:08

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.

 
Nahoru Odpovědět
15.6.2016 18:08
Avatar
Martin Dráb
Tvůrce
Avatar
Odpovídá na petr.dar
Martin Dráb:15.6.2016 18:13

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.

Nahoru Odpovědět
15.6.2016 18:13
2 + 2 = 5 for extremely large values of 2
Avatar
Martin Dráb
Tvůrce
Avatar
Martin Dráb:15.6.2016 18:18

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.

Nahoru Odpovědět
15.6.2016 18:18
2 + 2 = 5 for extremely large values of 2
Avatar
Martin Dráb
Tvůrce
Avatar
Odpovídá na petr.dar
Martin Dráb:15.6.2016 18:20

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).

Nahoru Odpovědět
15.6.2016 18:20
2 + 2 = 5 for extremely large values of 2
Avatar
Odpovídá na petr.dar
Neaktivní uživatel:15.6.2016 18:25

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.

Nahoru Odpovědět
15.6.2016 18:25
Neaktivní uživatelský účet
Avatar
Odpovídá na Martin Dráb
Neaktivní uživatel:15.6.2016 18:30

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.

Nahoru Odpovědět
15.6.2016 18:30
Neaktivní uživatelský účet
Avatar
Martin Dráb
Tvůrce
Avatar
Odpovídá na Neaktivní uživatel
Martin Dráb:15.6.2016 18:38

K tomuto účelu by asi sloužil move assignment/con­strutor (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.

Nahoru Odpovědět
15.6.2016 18:38
2 + 2 = 5 for extremely large values of 2
Avatar
petr.dar
Člen
Avatar
Odpovídá na Martin Dráb
petr.dar:15.6.2016 21:19

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 :-)

 
Nahoru Odpovědět
15.6.2016 21:19
Avatar
Martin Dráb
Tvůrce
Avatar
Odpovídá na petr.dar
Martin Dráb:15.6.2016 21:38

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.

Nahoru Odpovědět
15.6.2016 21:38
2 + 2 = 5 for extremely large values of 2
Avatar
Odpovídá na Martin Dráb
Neaktivní uživatel:15.6.2016 22:00

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íš.

Nahoru Odpovědět
15.6.2016 22:00
Neaktivní uživatelský účet
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 22 zpráv z 22.