Hirdetés

Keresés

Új hozzászólás Aktív témák

  • dobragab

    addikt

    válasz cadtamas #4190 üzenetére

    Most, hogy mar erted is, hadd szidjam kicsit a feladatot.

    A legnagyobb problema a Rectangle-ben, hogy redundans adattagok vannak benne. Alapvetoen ez egy olyan teglalapot tarol, aminek az oldalai parhuzamosak a tengelyekkel, tehat kb. ennyi adattagra lenne szukseg:

    Point topLeft;
    int width;
    int height;

    vagy

    Point topLeft;
    Point bottomRight;

    Hiszen ebbol a kettobol ki lehet szamolni az osszes tobbi adatot, egyetlen osszeadassal / kivonassal. Amugy a getter ertelme pont ez, hogy ha valami nem kozvetlenul all rendelkezesre, akkor is ki tudja szamolni / ossze tudja rakni. Ha ennel tobb adatod van, akkor sziszifuszi harc ezt konzisztensen tartani (ertsd: ne legyenek benne egymasnak ellentmondo adatok), ezert 3-5 soros egy setter. Igy kene mondjuk:

    Point getLowerLeft() const
    {
    return Point(topLeft.getX(), bottomRight.getY());
    }

    A masodik baj, hogy tobb setter, pl. a setTopLeft fuggveny szemantikailag ertelmetlen. Egy teglalap bal felso sarkat hogy allitod? Tolod az egesz teglalapot, hogy a merete maradjon meg, vagy megnyujtod, es 2 pontot mozgatsz vele, hogy a jobb also pont maradjon a helyen? Mivel nem egyertelmu, hogy mit csinal, nem szabadna ilyen nevu fuggvenynek lennie. At lehetne nevezni mondjulk pushTopLeft-re vagy dragTopLeft-re, ugy mar egyertelmu, hogy a teglalapot nyujtja, mint PowerPointban a kep atmeretezese.

    Altalaban veve pedig a setter rossz omen, azt sugja (haladobbaknak: code smell, tehat nem feltetlenul rossz, csak nagyon nagy valoszinuseggel), hogy valami nem oke a koddal. (haladobbaknak: Single Responsibility Principle-t sert, konkretan ugy, hogy nincs is responsibility.) Itt pl. az, hogy semmit nem csinal az osztalyod, semmivel sem jobb, mint

    struct Point { int x; int y; };

    A harmadik problema, hogy a Pont-nak kene konstruktor setter helyett.

    struct Point
    {
    int x;
    int y;
    Point()
    {
    x = 0;
    y = 0;
    }
    Point(int x, int y)
    {
    this->x = x;
    this->y = y;
    }
    };

    Es igy nem kell bele setter sem, mert felul tudod irni az = operatorral, pl.

    topLeft = Point(topLeft.x, topLeft.y + 10);

    Destruktort megmutatni semmi ertelme, amig nincs eroforraskezeles (tipikusan: dinamikusan foglalt memoria), es a Rectangle osztalyban amugy sem szabad ilyennek lennie (egy teglalap ne kezeljen mar dinamikus adatot, haladobbaknak: ez megint SRP-t sert). De ezt irtak elottem is.

    Negyedik: hibas a Point ket settere (ami amugy nem is kene, lasd fent). Igy lenne helyes:

    void setX(int x) { itsX = x; }
    void setY(int y) { itsY = y; }

    Igy hirtelen ennyi, ami bokte a csorom. Azert irtam le, mert az OOP-nek (objektumorientalt programozas) jo lenne, ha nem csak a szintaktikai elemeit tanitanak meg, hanem az elveket, ami menten epiteni es alkalmazni illik. Anelkul semmi ertelme az egesznek.

    OFF: amugy sajnos nem talaltam olyan konyvet, ahol le vannak ezek irva rendesen, es nem 500+ oldalban. Ha valaki tud ilyet, ne tartsa magaban.

    [ Szerkesztve ]

    Tudom, tudom, akasszak a tökömre egy lámpát, hogy sötétben is tudjak kaszálni.

  • mgoogyi

    senior tag

    válasz cadtamas #4193 üzenetére

    Az implementáció mondja meg, hogy mit csinálnak a függvények. Anélkül nincs program, csak a függvények fejlécei. Az implementációt találod a cpp fileban.
    A .h-ban megmondod, hogy ilyen-olyan függvényeid vannak az osztályban, meg ilye-olyan adattagjaid.
    A .cpp-ben meg elvégzed az érdemi munkát, azaz kifejted(implementálod), hogy mit csinál a konstruktor, mit csinál a settop és a többi függvény. Ha ezt nem tennéd meg, a program futásakor nem lenne semmi a függvényhívások mögött.

    A privát változókat az osztály saját függvényei látják, egyedül előttük nem rejtett. Ha legalább ők nem látnák, akkor semmi értelme nem lenne a privátnak.
    Ezek az osztály saját belső állapotát tükroző változók, nem lokálisak. A lokálisnak látnád a deklarációját a függvény elején, pl. int itsTop;

    Az osztályra meg úgy gondolj, mint egy olyan valamire, aminek a belső változói adják az állapotát és a függvények pedig azt módosítják vagy azt felhasználva csinálnak valamit.

    Amikor a konstruktor lefut, akkor éppen frissen létrejött az osztályodból egy új objektum. A konstruktor a kezdeti állapotát állítja be az objektumodnak. Ezután az objektumon az osztály minden publikus függvényét meghívhatod, ami konkrétan azon az objektumon fog dolgozni. Egy osztályból annyi objektumot csinálsz(példány példányosítasz), amennyit akarsz. Mindegyiknek meglesz a saját belső független állapota, saját itsTop, itsRight stb. belső értékekkel.

    [ Szerkesztve ]

  • mgoogyi

    senior tag

    válasz cadtamas #4190 üzenetére

    //Ez elvileg az implementáció, amit nem taglal a könyv, hogy miért kell és később miért nem használjuk semmire.
    -> Az implementáció írja le a dolgok működését, a .h-ban meg azt látod, hogy mit tudsz csinálni majd ezen az osztályon

    //ide kellene gondolom a destruktor, ami azért jó, mert memóriát szabadít fel?
    -> vagy egyéb resource-öket enged el, de az osztályodban nincs ilyen. Akkor fogsz látni destruktort, ha az osztályodban lesz olyan, hogy "new"

    setupperleft:
    //módosítja a JobbFelső pont Y értékét, de miért???
    -> azért, mert ez egy téglalap, ha a bal felső sarkát feljebb húzod, a jobb felsőnek is követnie kell

    //a test felső vonalát határozza meg, de nem ez a dolga!!!
    -> dehogynem, az itstop értékét, azaz mi a legnagyobb Y értéke a téglalapnak, módosítani kell, amikor valamelyik sarkát arrébb teszed

    setTop(int top):
    //beállítja a test felső vonalát. Ugyanazt mint a setUpperLeft() függvény. MIÉRT???
    -> azért, mert ez a függvény a téglalap felső oldalát teszi arrébb, amivel együtt mozognak a felső sarkai

  • b.kov

    senior tag

    válasz cadtamas #3877 üzenetére

    Szia!
    Fentebb linkeltem egy nagyon jó, részletes, magyar nyelvű anyagot.
    Abban érdemes lenne utána nézned a dolgoknak, érthetően le van írva minden. :)

    Link

    [ Szerkesztve ]

  • dabadab

    titán

    válasz cadtamas #3877 üzenetére

    Ha Pythont ismered, ott minden paraméter referenciaként adódik át, sőt, ott gyakorlatilag az összes változó referencia, ami szerintem elég vicces működést tud produkálni meg rengeteg fejtörést azoknak, akik nem igazán értik, hogy mi a referencia.

    Egyébként visszatérve azért, hogy miért jobb a referenciaként átadott paramétert módosítani, mint a visszatérési értéket használni: mert sokkal hatékonyabb. Ha tisztán érték szerint átadott, majd visszatérési értékkel átírt objektumnál háromszor kell a komplett objektumot (ami csatolt részeivel együtt akármekkora lehet, akár gigabyte-os méretű is!) háromszor kell mindenestül átmásolni (egyszer az érték szerinti átadásnál, másodszor akkor, amikor ezt átmásoljuk visszatérési értéknek, harmadszor meg akkor, amikor a visszatérési értéket beírjuk a régi helyére), referenciakénti átadáskor meg nullaszor. Egyébként ezért van az, hogy C++-ban (a primitív típusokat (int, double, boolean, ilyenek, amik beleférnek egy regiszterbe) leszámítva) minden paramétert referenciaként szokás átadni, legfeljebb kap egy constot, hogy tudjuk, hogy ahhoz nem fog hozzápiszkálni a függvény.

    DRM is theft

  • dobragab

    addikt

    válasz cadtamas #3875 üzenetére

    Némi terminológia, hogy eligazodj azon a könyvön túl is:

    mutató == pointer
    hivatkozás == referencia

    Ha a függvény feladata egy változó módosítása, akkor miért nem adjuk meg a függvény visszatérési értékének ezt az értéket és írjuk felül az eredeti változót?

    Mert nem mindig lehet megtenni, vagy logikailag nincs értelme, vagy 2 értéket kéne visszaadnia, amit nem lehet, stb.

    Tudom, tudom, akasszak a tökömre egy lámpát, hogy sötétben is tudjak kaszálni.

  • dabadab

    titán

    válasz cadtamas #3873 üzenetére

    (Jézusom, ez a példaprogram... az már önmagában elég ahhoz, hogy azt javasoljam, hogy inkább tanulj valami másból)

    Szóval: a mutató egy olyan változó, ami egy másik változó vagy objektum helyére (címére) mutat a memóriában. Ez eddig megvan? Itt a két fő koncepciók az, hogy van olyan, hogy a memóriacím (a számítógépben a byte-ok meg vannak számozva nullától kezdve nagyonsokig és ezzel a számmal lehet rájuk hivatkozni) és hogy minden változó meg objektum valójában egy rakat byte-ként létezik a számítógépben.

    A hivatkozás az gyakorlatilag egy mutató, csak a szintaxisban van különbség: ott feltételezik, hogy minket elsősorban a mutatott dolog érdékel, nem maga a memóriacím és így nem várja el a fordító hogy csillaggal meg nyillal bohóckodjunk, megspórolva némi gépelést meg olvashatóbbá téve a kódot.

    Miért jó, hogy vannak ilyenek?

    A fő felhasználási terület a függvények paramétereinél van. A paramétereket alapvetően átadhatjuk értékként vagy hivatkozásként: ha értékként adjuk át, akkor ha a függvényben bizergáljuk, az nincs kihatással az eredeti változóra - ha hivatkozásként, akkor meg igen.

    A másik felhasználás az, amikor new-val hozunk létra objektumokat, hogy az élettartamuk független legyen annak a függvénynek az élettartamától, amelyik létrehozza őket (ugye a helyi változók csak addig élnek, amíg a függvény le nem fut - ha new-val hozol létre valamit, az meg addig, amíg rá nem küldesz egy delete-et).

    DRM is theft

Új hozzászólás Aktív témák