C++ no default constructor exists for class

Idris

  • *****
  • 2 286
    • Zobrazit profil
    • E-mail
Re:C++ no default constructor exists for class
« Odpověď #15 kdy: 22. 09. 2019, 15:30:45 »
Ta třída má jednu zásadní vadu: Nic neumí. Vyvaruj se tvorby takových anemických tříd, dej do nich alespoň jednu metodu.

Kromě toho, že to je ukázkový příklad, tak třída  (struktura) v C++ nemusí nic "umět". C++ není OO jazyk (ala C#/Java), klidně se dá pracovat se strukturama a volnýma funkcema ... nakonec popírá to OOP?

V tom případě může rovnou použít struct nebo třídu s veřejnými atributy (resp. read-only) a vyjde to nastejno - spíš lépe.

Ale potom nebude immutable. Snažil som sa naemulovať immutable štruktúru ktorá sa dá len čítať. Okrem toho ja ako funkcionálny programátor nemám nič proti anemickým objektom.

Immutable je něco jiného než read-only?
Ano.


Re:C++ no default constructor exists for class
« Odpověď #16 kdy: 22. 09. 2019, 22:01:06 »
C) občas je k vidění varianta "maximálně úsporná" = jména symbolů jsou 2-4 znaky dlouhé zkratky :-) Viz třeba zdrojáky Perlu.
...
( C) je samozřejmě blbost s Perlem nesouvisející, je rozdíl mezi built-in/idiomatickými proměnnými a tím, jak se pojmenovávají "uživatelské" proměnné.)
[/quote]

O nic nejde - nejsem si jist jestli si správně rozumíme, pro jistotu upřesním: měl jsem na mysli Céčkové zdrojáky samotného Perl interpretru, viz např. datový typ SV znamená Scalar Value, třeba tady na řádku 321 atd. (deklarace 'SV* sv;' a návazná užití té proměnné). Celá perlová střeva jsou špikována takto pojmenovanými elementárními typy. Připomíná to písničku "Zkratky" od Ivana Mládka.

Re:C++ no default constructor exists for class
« Odpověď #17 kdy: 22. 09. 2019, 23:13:54 »
Prechádzam z viacerých jazykov (C#, F#, JS, TS, PHP) všetky majú triedy na jedno kopyto, a nikde som sa nestretol s tým aby som takýmto spôsobom musel inicializovať aj vnoreného membera, vždy sa to takto robilo iba pri predkoch. A rád by som počul vysvetlenie prečo je to nutné aj pri vnorených objektoch.

...je možné, že jsem nepochopil správně otázku, protože o zmíněných jazycích toho vím málo. Zjevně postupujete od jazyků vyšší úrovně dolů, směrem ke "kvalitnímu assembleru" :-)

Tedy "proč povinně inicializovat vnořené membery" : soudím, že se jedná o základní projev zásady RAII. Rodí se instance objektu, a v okamžiku zrodu je třeba ji uvést do nějakého smysluplného/konzistentního počátečního stavu. Tzn. dát nějaký smysluplný počáteční stav všem obsaženým memberům. Třeba je všecky jenom vynulovat - ale prostě jasně definovaný stav, na který se následně můžete spolehnout. Spolu se to alokuje, tak se to má spolu taky inicializovat.
Osobně jsem se několikrát dostal do situace, kdy nebylo možné implementovat nějaké další inicializační kroky v konstruktoru. To je v pořádku, další životní oblouk či stavový mechanismus právě vzniklé instance je už Vaše odpovědnost.
Buď se dá objekt na této nízké úrovni inicializovat v konstruktoru na hodnoty "já jsem zatím jenom prázdná skořápka". Nebo se třeba problematičtí "membeři", ke kterým zatím nemáte co říct, dají vyčlenit do samostatných objektů, na které se odkážete pointerem (nebo možná C++ referencí, nebo odkazy na ně zabalíte do nějakého kontejneru, který může mít do začátku nulový počet prvků apod.) - takové "externě odkázané membery" pak nemusíte inicializovat současně s "držitelskou instancí"... čímž ovšem za ně pochopitelně přebíráte odpovědnost.
Pokud jsem otázku nepochopil, tak se omlouvám :-)

Zmínil jste, že v jiných jazycích se povinně inicializují jenom "věci zděděné od předka", nikoli vlastní membeři. Což mi off topic připomnělo kapitolu od Bruce Eckela zvanou Inheritance and Composition (možná lépe najít v obsahu kap.č.14)

Re:C++ no default constructor exists for class
« Odpověď #18 kdy: 23. 09. 2019, 15:10:18 »
C) občas je k vidění varianta "maximálně úsporná" = jména symbolů jsou 2-4 znaky dlouhé zkratky :-) Viz třeba zdrojáky Perlu.
...
( C) je samozřejmě blbost s Perlem nesouvisející, je rozdíl mezi built-in/idiomatickými proměnnými a tím, jak se pojmenovávají "uživatelské" proměnné.)

O nic nejde - nejsem si jist jestli si správně rozumíme, pro jistotu upřesním: měl jsem na mysli Céčkové zdrojáky samotného Perl interpretru, viz např. datový typ SV znamená Scalar Value, třeba tady na řádku 321 atd. (deklarace 'SV* sv;' a návazná užití té proměnné). Celá perlová střeva jsou špikována takto pojmenovanými elementárními typy. Připomíná to písničku "Zkratky" od Ivana Mládka.

Tak to se omlouvám, opravdu jsem to pochopil špatně.
I když si stále nemyslím, že by to měl být nějaký "standalone" styl pojmenovávání proměnných, spíš prostě způsob jak si ulehčit psaní dlouhých jmen, když je to v tu chvíli jasné.

Kit

  • *****
  • 704
    • Zobrazit profil
    • E-mail
Re:C++ no default constructor exists for class
« Odpověď #19 kdy: 23. 09. 2019, 16:40:14 »
C) občas je k vidění varianta "maximálně úsporná" = jména symbolů jsou 2-4 znaky dlouhé zkratky :-) Viz třeba zdrojáky Perlu.
...
( C) je samozřejmě blbost s Perlem nesouvisející, je rozdíl mezi built-in/idiomatickými proměnnými a tím, jak se pojmenovávají "uživatelské" proměnné.)

O nic nejde - nejsem si jist jestli si správně rozumíme, pro jistotu upřesním: měl jsem na mysli Céčkové zdrojáky samotného Perl interpretru, viz např. datový typ SV znamená Scalar Value, třeba tady na řádku 321 atd. (deklarace 'SV* sv;' a návazná užití té proměnné). Celá perlová střeva jsou špikována takto pojmenovanými elementárními typy. Připomíná to písničku "Zkratky" od Ivana Mládka.

Tak to se omlouvám, opravdu jsem to pochopil špatně.
I když si stále nemyslím, že by to měl být nějaký "standalone" styl pojmenovávání proměnných, spíš prostě způsob jak si ulehčit psaní dlouhých jmen, když je to v tu chvíli jasné.

Ono by to bylo špatně i v případě, kdyby se ta proměnná jmenovala scalar_value.


Re:C++ no default constructor exists for class
« Odpověď #20 kdy: 25. 09. 2019, 22:46:04 »
Ďakujem Vám všetkým a hlavne Františkovi (ale aj ostatným).

Ospravedlňujem sa že som skôr nereagoval ale kým o tom nemám dostatok informácií tak nechcem vyzerať ako úplný analfabet. Už začínam chápať kde bol problém preto sa teraz študujem pointery a referencie aby som mal istotu čo kedy a kde použiť (A hlavne sa hráme s shared_ptr a unique_ptr) Teda viem ako tie pointery fungujú ale dosť často napriek tomu tápem či mám ten objekt alokovať na stacku alebo heape.

Re:C++ no default constructor exists for class
« Odpověď #21 kdy: 26. 09. 2019, 20:32:58 »
Ďakujem Vám všetkým a hlavne Františkovi (ale aj ostatným).

Ospravedlňujem sa že som skôr nereagoval ale kým o tom nemám dostatok informácií tak nechcem vyzerať ako úplný analfabet. Už začínam chápať kde bol problém preto sa teraz študujem pointery a referencie aby som mal istotu čo kedy a kde použiť (A hlavne sa hráme s shared_ptr a unique_ptr) Teda viem ako tie pointery fungujú ale dosť často napriek tomu tápem či mám ten objekt alokovať na stacku alebo heape.

Hmm, možná už je pozdě, ale neuvažoval jsi, že by ses místo C++ začal učit Rust? Jestli to máš na vlastní projekty, myslím, že by se ti mohl líbit víc, obzvlášť pokud píšeš že jsi programoval funkcionálně tě oproti C++ potěší ADT přímo v jazyce, absence NULL a výjimek, nemožnost data races nebo neplatného přístupu do paměti (double free, use after free, leaky).. Cílová skupina je asi ta samá, systémové programování. Ale jestli to hledáš kvůli komerčnímu uplatnění, tak toho je v C++ samozřejmě víc..
« Poslední změna: 26. 09. 2019, 20:39:14 od registrovany_ava »

Re:C++ no default constructor exists for class
« Odpověď #22 kdy: 27. 09. 2019, 00:15:42 »
Na stacku vs. na heapu:

A) Lokální proměnné uvnitř funkce (včetně argumentů deklarovaných v prototypu) se házejí na stack. V user space v moderním OS asi není problém, že by na stacku nebylo dost místa - ale je třeba si uvědomit, že věci na stacku mají omezenou životnost. Jakmile skončí dotyčná funkce, její lokální proměnné se vypaří (out of scope), tzn. stack pointer se vrátí o jeden stack frame výš (stack tradičně roste směrem dolů). Pokud jsou lokálně deklarované nějaké skutečné objekty, proběhne v rámci ukončení funkce jejich destruktor.

B) Instance vytvořené operátorem "new" jsou alokovány na heapu. Na takovou novou instanci dostanete pointer. Pokud takový pointer máte deklarovaný jako lokální proměnnou ve funkci (běžná situace) tak samotný pointer samozřejmě skončí s touto funkcí, ale odkazovaná instance na heapu žije dál. Pokud by Vás to téma zajímalo, tak C++ sice nemá garbage collector, ale alokaci různě velkých věcí na heapu řídí tzv. allocator. Přiděluje a uvolňuje místo, vede si přehled o jednotlivých alokacích, snaží se bránit fragmentaci (ve spolupráci s memory managementem OS = záleží i na podvozku).

C) Popravdě mi není z hlavy úplně jasné, kde jsou alokovány proměnné/objekty, deklarované jako globální v konkrétním Cčkovém souboru (translation unit). Patrně se stanou součástí spustitelného binárního souboru (nebo knihovny) a mají tím alokované místo. Tzn. představte si takový objekt jako "bublinu" uvnitř nějakého toho ELF nebo PE-COFF bináru - do RAMky se dostane zavedením toho bináru ke spuštění. Jisté je, že životnost těchto globálně deklarovaných objektů je shodná se životností běžícího executable bináru - pouze se při startu na to před-alokované místo poštve konstruktor a při ukončení destruktor.


Pak jste psal, že si hrajete se strong pointery... schválně zkusím přihodit střípek své naivní tvorby. "Manuálně invazivní" reference counting šablonu a k ní komplementární "chytrý pointer" (taky šablona). Vytvoříte pointer na nějakou svoji instanci obsahující tohoto refcounting parazita, a parazit ví, že na něj někdo odkazuje. Takových pointerů může být víc. Pokud ten smart pointer projde destrukcí, refcount v odkazované instanci se automaticky sníží. Pokud spadne refcount na nulu, proběhne autodestrukce hostitelské instance refcounting parazitem. Pokud byste chtěl nesmrtelnou instanci, buď si naalokujte navrch jeden smart pointer, který nikdy nezničíte, nebo tu referenci prostě explicitně inkrementujte... (Ano, hrozně rád vynalézám kolo.)


Jojo. První krůčky s C++.

Mně osobně se v začátcích hodně líbila klasická poučka, že abstraktní třída musí mít čistě virtuální destruktor, ale musí pro něj mít definované tělo :-) Nebo jak to vlastně bylo... V původní podobě jsem tu poučku našel tady. Nicméně už Bruce Eckel ve druhém vydání Thinking in C++ tvrdí, že tahle praxe je překonaná, že dle novějšího standardu má být ten destruktor prostě jenom virtuální (s definovaným tělem = tohle platí dál).

Pak se dá dostat do situací, kde bojujete se zacyklenými deklaracemi tříd. Dalo by se ještě pochopit, že pokud B je memberem A, nelze zároveň deklarovat že A je memberem B. Nekonečná rekurzivita vnoření se nekoná. Ale do podobných lapálií se můžete dostat při použití šablonových tříd. Situaci nepomáhá, že šablony prakticky komplet žijí v hlavičkových souborech, takže nelze použít fintu "v hlavičkách se to jenom nadeklaruje a pak v těle (souboru CPP) se dořeší zbytky". Tuším jsem se asi dvakrát dostal do kouta, ze kterého jsem nenašel jinou cestu, než obalit jednu ze tříd do memberu typu void* :-)

Prostě zažívám okamžiky C++ WAT :-)

Popravdě mi šly oči trochu křížem z tohoto řádku ve Vašem zdrojáku:
  typedef function<void(KeyboardShortcut)> KeyboardCallback;
...ale nakonec pokud správně chápu, nejedná se o nic jiného než v Céčkové tradiční notaci
  typedef void (*KeyboardCallback)(KeyboardShortcut);
...čili žádné velké funkcionální utrpení :-)
Já jsem totiž krajně procedurální tvor...