Útmutató a 4. laborhoz

A laborgyakorlaton a Jporta feladatban kiadott Resistor osztályt kell bővíteni. A laborfeladatokkal a 3. előadáson bemutatott alapértelmezett (implicit deklarált) tagfüggvények (konstruktorok, destruktor, értékadó operátor) szerepét és felhasználását gyakorolhatja.
A laborgyakorlat elvégzésének előfeltétele a Cpp Ellenállás nevű feladat sikeres megoldása.

Felkészülés a laborra

  1. Nézze át a 4. előadás anyagát, és példaanyagát!
  2. Ismételje át a 3. heti laborgyakorlat anyagát!
  3. Oldja meg a Jporta Cpp Ellenállás nevű feladatát
Jól jegyezze meg: .h fájlba csak deklarációt, valamint makró, inline függvények, és sablonok definícióit szabad írni.

Laborfeladatok megoldásokkal

A laborfoglalkozás végén töltse fel az elkészített elkeszult.h és resistor.cpp fájlokat a Jporta rendszerbe (Resistor, önellenőrző feladat), hogy megkapja a labor elvégzéséért járó pontot! Legalább az első öt tesztesetet (ELEKESZULT >= 5) hibátlanul meg kell oldania! A feladatok feltöltésére a laborgyakorlatot követő szombat 06:00-ig van lehetősége.

Előkészítés

    UNIX/Linux környezetben a make paramétereként adja meg, hogy melyik változatot akarja előállítani (resistor_test, resistor_test2, potmeter_teszt)
  • CPP Ellenállás feladat „beüzemelése”: Ehhez töltse le a laborfeladathoz előkészített összes fájlt a tárgy Git tárolójából: https://git.ik.bme.hu/Prog2/labor_peldak/lab_04! A letöltött projekthez adja hozzá a Ön megoldását, azaz másolja be a Jporta-n megoldott feladatából a resistor.cpp fájlt! Fordítsa le, próbálja ki! Ha nem sikerült hibátanul megoldania a Cpp Ellenallas feladatot, úgy kérje a laborvezető segítségét!
  • Tipp: Törölje a projektből a resistor_test.cpp fájlt, és adja hozzá a resistor_test2.cpp-t.
  • Ha működik feladat, cserélje le a főprogramot a laborgyakorlathoz előkészített resistor_test2.cpp-vel! Az eredmények egyszerűbb ellenőrzéséhez ebben a változatban a korábban is bemutatott gtest_lite keretrendszert használjuk. Írja be az első tesztesetbe (999999 helyére) az Önnek kiadott CPP Ellenállás feladat specifikációja által megkövetelt ellenállásértéket! Fordítsa le a programot és futtassa. Ha mindent jól csinált, akkor 6 tesztnek kell futnia és nem lehet hiba (feltéve, ha megoldotta korábban a "CPP Ellenallas" Jporta fealdatot).
  • A resistor.h fájlban megvalósítottunk egy Pr() makrót, amit a további feladatokban egyszerű üzenetek kiírására használhat. Ezzel egyszerűen elhelyezhet kiírásokat, melyek csak akkor jelennek meg, ha a DEBUG makró definiált. A DEBUG makrót az előkészített projektfájlokban definiáltuk. Ha nem használja ezeket a projektfájlokat, akkor ne felejtse el a saját fejlesztői környezetében (esetleg a resistor.h-ban) a DEBUG makrót definiálni!

Feladatok

  1. A főprogramban (resistor_test2.cpp) keresse meg és jelölje meg megjegyzéssel azokat a sorokat, amelyek konstruktorokat (nem másoló) hívnak! A tesztesetek neve segít, de az is elképzelhető, hogy konstruktorhívás nem közvetlenül a resistor_teszt2.ccp fájlban van!

    Ezután a resistor.cpp fájlban megvalósított konstruktorokat egészítse ki kiírásokkal, hogy ellenőrizhesse elgondolását! A paraméter nélkül hívható konstruktor írja ki a standard kimenetre, hogy "ctor0", az egyparaméteres pedig, hogy "ctor1"! A kiírásokhoz használja a Pr() makrót! Az elkeszult.h fájlban állítsa az ELKESZULT makró értéket 1-re! Fordítson, futtasson! Egyik ablakban nyissa meg a főprogramot, ahova a kommenteket beírta, a másikban nézze a futás eredményét! Ezeket a kiírásokat várta? Ha nem, értse meg, hogy mi az eltérés oka!

  2. A fordítóknak lehetősége van egyes konstruktor hívásokat, ideiglenes váltózók használatát összevonni. Ezt tiltja a gcc ill. clang fordítónál a -fno-elide-constructors kacsoló. Az előkészített CodeBloks projektfájlban ez be van kapcsolva, hogy könnyebben megérthető legyen, mikor melyik konstruktor hívódik. Ha nem az előkészített projektfájlt hasnnálja, akkor célszerű a saját fejlesztői környezetében ezt, illetve ennek megfelelő opciót bekapcsolni, ha van ilyen! MSVC esetében ilyen kapcsoló nem ismert.
  3. Előadáson látta, hogy léteznek speciális tagfüggvények, amelyek implicit módon keletkeznek, ha azokat explicit módon nem deklarálják (alapértelmezett tagfüggvények). Egyik ilyen tagfüggvény a másoló konstruktor. A főprogramban keresse meg és jelölje meg megjegyzéssel azokat a sorokat, amelyek másoló konstruktort hívnak! Számolja meg, hány ilyen helyet talált!
    Tipp: A resistor.h fájlban megtalálja a másoló konstruktor deklarációját! Másolja azt át a resistor.cpp-be! Ügyeljen a helyes scope használatra!

    Valósítsa meg a resistor.cpp fájlban a Resistor osztály másoló konstruktorát úgy, hogy az jól működjön és írja ki a standard kimenetre, hogy "copy"! A kiíráshoz használja a Pr() makrót! Ne felejtse el az adattag inicializálását, azaz a megfelelő másolat előállítását sem! Az explicit másoló konstruktor meghívja az adattagok másoló konstruktorát (inicializálja azokat).

    A másoló konstruktor formálisan egy olyan konstruktor, aminek egyetlen, az osztályra hivatkozó const referencia paramétere van! Az adattag(ok) inicializálása formálisan helyesen csak inicializáló listán történhet (C++11 előtti változatokban). Egyéb esetben inicializálatlanul jönnek létre a tagváltozók, amit persze értékadással módosíthatunk, de így először létrejön a tagváltozó a paraméter nélkül hívható konstruktorral, majd ennek értéket adunk.

    Állítsa az ELKESZULT makró értéket 2-re! Fordítson, futtasson! Az előző feladathoz hasonlóan elemezze a kiírásokat! Célszerűen az egyik ablakban a forrást, a másik ablakban a futás eredményét nézze! Ezeket a kiírásokat várta? Ha nem, akkor

    • Tegyen a töréspontot másolókonstruktorba és hajtsa végre a programot úgy, hogy a másolónál mindig álljon meg és nézze meg, hogy az honnan hívódott! (Debug --> Debuging windows --> Call stack)
    • Kérje laborvezetője segítségét, ha önállóan nem jut előre!
  4. Tipp: A resistor.h fájlban megtalálja a destruktor deklarációját! Másolja azt át a resistor.cpp-be!
  5. A főprogramban keresse meg és jelölje meg megjegyzéssel azokat a sorokat, amelyek destruktort hívnak! Valósítsa meg a resistor.cpp fájlban a Resistor osztály destruktorát, ami kiírja, hogy "dtor"! A kiírásokhoz használja a Pr() makrót! Állítsa az ELKESZULT makró értéket 3-ra! Fordítson, futtasson! Ezeket a kiírásokat várta?
  6. A főprogramban keresse meg és jelölje meg megjegyzéssel azokat a sorokat, amelyek értékadó operátort hívnak! Valósítsa meg a resistor.cpp fájlban a Resistor osztály értékadó operátorát úgy, hogy az helyesen működjön és írja ki, hogy "assign"! A kiírásokhoz használja a Pr() makrót! Állítsa az ELKESZULT makró értéket 4-re! Fordítson, futtasson! Ezeket a kiírásokat várta?
  7. Az eredeti feladatot (CPP Ellenallas) tekintve a paraméter nélkül hívható konstruktor kivételével minden explicit deklarált tagfüggvény megfelelő lenne. Az implicit módom megadott függvényekre csak a kiírások miatt van szükség.

    Elemezze a kiírásokat! Fontos, hogy pontosan értse, hogy melyik kírás miért jelent meg a képernyőn! A Resistor.osszeg tesztesetben pl. miért jelenik meg "copy"? Hol van abban másoló konstruktor hívás? Ha pontosan érti minden kiírás hátterét, kapcsolja azokat ki, hogy a további feladatok megoldásában ne zavarjanak! Fordítsa le újra az egész projektet (Rebuild)!

    Törölje a DEBUG makró definícióját a projekt fájlból, (vagy a resistor.h-ból, ha odatette a definíciót korábban)! A feltételes fordítást vezérlő makrók definícióját a projekt/Make fájlba tenni bonyolultabbnak tűnik, de nagy projekteknél megtérül ez a látszólagos bonyodalom.
    Az resistor.h fájlban már deklaráltuk a getI(), getU() függvényeket.
  8. Készítsen tagfüggvényt, melynek segítségével lekérdezhető az ellenálláson eső feszültség, illetve az átfolyó áram (getI(double),getU(double))! Állítsa az ELKESZULT makró értéket 5-re (getI()), majd 6-ra (getU())! Fordítson, futtasson!
  9. Szorgalmi feladatok

  10. Bővítse az osztályt összehasonlító operátorral (==) ! Állítsa az ELKESZULT makró értéket 7-re! Fordítson, futtasson!

    Ha az egyenlőségvizsgálat tesztje hibát jelzett, akkor nagy valószínűséggel elkövette azt a hibát, hogy két valós számot hasonlított össze == operátorral. Gondolja át az előző félévben a valós számok ábrázolásáról tanultakat! A glite_test.h fáj legvégén talál egy almostEQ sablont. Használja azt megoldásában!
  11. Készítsen inserter (<<) operatort, amivel egy ellenállás kiírható egy std::ostream objektumra! Állítsa az ELKESZULT makró értéket 8-ra! Fordítson, futtasson!
  12. IMSC feladatok

  13. Vegyünk egy potmétert melynek a névleges ellenállása legyen R (a két végkivezetés között). Az egyik végpont és a középső pont között az ellenállás a névleges érték (R) 1%-a és 99%-a között változtatható a kezelőszerv forgatásával.
    Modellezzük a potméter két végpontjának összekötésével létrejövő áramköri elem működését, amit úgy használunk, hogy a középkivezetést és az összekötött végpontokat kötjük be az áramkörbe. Valahogy így:

    A végpontok összekötésével létrejövő elem (SimPoti) működése két párhuzamosan kötött változtatható ellenállással (R1, R2) modellezhető, azaz az eredő ellenállás R1 % R2, ahol:
    % a korábban bevezetett párhuzamos kapcsolást jelenti,
    R1 + R2 = R,
    R1 = R*POS/100,
    R2 = R*(100-POS)/100
    POS - a kezelőszerv állása: 1..99
    Tervezze meg, majd valósítsa meg a SimPoti osztályt! Legyen az osztálynak:
    • Konstruktora, amivel megadható a potméter névleges ellenállása! A konstruktor középre (50%) állítsa a potméter kezelőszervét!
    • Legyen lekérdezhető a kezelőszerv aktuális állása!
    • Legyenek olyan tagfüggvények (pre-inkremens, és post-dekremens operator), amivel a kezelőszerv állása 1-1%-kal növelhető ill. csökkenthető! A legkisebb állás 1, a legnagyobb 99 legyen!
    • Legyen lekérdezhető a mindenkori eredő ellenállás

    A megvalósításhoz használja fel a Resistor osztályt, de azt ne módosítsa!
    Ajánlott lépések:

    • Cserélje le ismét a főprogramot! Az új főprogram a potmeter_teszt.cpp legyen! Adja hozzá a projekthez a potmeter.h, potmeter.cpp, console.h, console.cpp fájlokat.
    • Tanulmányozza a potmeter.h fájlt, és készítse el a hiányzó tagfüggvényeket a potmeter.cpp fájlban!
    • A tanulmányozza a potmeter_teszt.cpp fájl teszt- és használati eseteit! A fájlban pár egyszerű teszteset után egy interaktív részt is talál, amiben az ellenállást billentyűnyomásra változtatható nagyságú áram, ill. feszültséggenerátorra kötjük és kvázi-grafikusan kijelezzük a mért értékeket. A konzol ablak kezelését a console.cpp fájlban definált Console osztály végzi.

Jó munkát!

Szeberényi Imre

Utolsó frissítés: 2021-02-26 16.47