Kurzusok
/
C++
C++
/
Feladatok
/
Speedywagon Alapítvány
Speedywagon Alapítvány

Speedywagon Alapítvány

Tanulófeladat

Bevezetés

Sok más nyelvhez hasonlóan a C++ is ismeri a mutatókat. Már tudod, hogy a referenciák és a mutatók hasonlítanak egymásra, de gondolj rájuk úgy, mint ami egy szinttel közelebb van a számítógéped belső működéséhez. A mutatók olyan változók, amelyek objektumok címét tárolják. Segítségükkel közvetlenül dolgozhatsz az objektumokkal, ami lehetővé teszi a dinamikus memóriafoglalást és a hatékony adatkezelést a C++-ban.

Ha most ismerkedsz a mutatókkal, elsőre kissé rejtélyesnek tűnhetnek, de ha hozzászoksz, egészen egyszerűek.

A C++ kulcsfontosságú részét képezik, ezért szánj rá időt, hogy igazán megértsd őket. Az ebben a fogalomban bemutatott csupasz változatot buta mutatónak vagy nyers mutatónak is szokás nevezni. A modern C++-ban okos mutatók is léteznek, a legalaptípus viszont egyáltalán nem okos, és minden munkát neked kell kézzel elvégezned.

Mielőtt belemennénk a részletekbe, érdemes megérteni, mire használjuk a mutatókat. A mutatók révén megoszthatjuk egy objektum címét a programunk más részeivel, ez pedig két fő okból hasznos:

  1. A referenciákhoz hasonlóan a mutatók elkerülik a másolatokat, és segítenek csökkenteni a program erőforrásigényét.
  2. A referenciákkal ellentétben a mutatók átirányíthatók más objektumokra.
  3. A mutatók mutathatnak null értékre is, jelezve, hogy éppen semmilyen objektumra nem mutatnak.

Általános szintaxis

A C++-ban egy mutató deklarációjakor meg kell adnod azt az adattípust, amelyre a mutató mutat, majd egy csillagot (*) és a mutató nevét. A mutatók deklarálásakor azok nem kapnak automatikusan kezdőértéket. Explicit értékadás nélkül a mutató jellemzően határozatlan értéket tárol, amit gyakran „szemétcímnek” neveznek. Bár bizonyos fordítók nullptr-re inicializálhatják a mutatókat, ez a viselkedés nem garantált minden fordítónál, ezért fontos, hogy ne számíts rá. A legjobb gyakorlat, ha a nyers mutatókat explicit módon inicializálod, és használat előtt ellenőrzöd, hogy nem null értékűek, így elkerülheted a lehetséges problémákat.

int* ptr{nullptr}; // Declares a pointer and makes sure it is not invalid

Ahhoz, hogy egy változó címét egy mutatóhoz rendeld, a címképző operátort (&) használod. Egy mutató visszafejtése az indirekciós operátorral (*) történik.

std::string opponent{"Solomon Lane"};
// 'ethan' points to the address of the string opponent
std::string* ethan{&opponent};
// Instead of ethan's, the opponent's name address is given to the passPort
std::string passportName{*ethan};

Figyelem: a visszafejtést explicit módon kell elvégezni, a referenciák pedig egyszerűen aliasként viselkedtek.

Mutatóaritmetika

A mutatóaritmetika lehetővé teszi, hogy aritmetikai műveleteket végezz a mutatókon, ami különösen tömbökkel dolgozva hasznos. Ha egy egész számot adsz egy mutatóhoz, az egy másik elemre fog mutatni.

// Stargate Coordinate Code
int gateCode[] = {462, 753, 218, 611, 977};
// 'ptr' points to the first element of 'gateCode'
int* ptr{&gateCode[0]};
// Accesses the third Stargate address through pointer arithmetic
int dialedAddress{*(ptr + 2)};
// Chevron encoded! Dialing Stargate address:
openStarGate(dialedAddress);
Caution

A C++-ban a mutatóaritmetika könnyen nem definiált viselkedéshez vezethet, ha nem bánsz vele óvatosan. A nem definiált viselkedés váratlan programeredményekben, összeomlásokban vagy akár biztonsági résekben is megnyilvánulhat. A nem definiált viselkedés következményeire híres példa az Ariane 5 rakéta felrobbanása 1996-ban, amikor egy 64 bites lebegőpontos szám 16 bites előjeles egésszé alakításából eredő szoftveres kivétel katasztrofális meghibásodáshoz vezetett.

Tagváltozók elérése

A C++-ban a -> operátorral érheted el egy objektum tagjait egy rá mutató mutatón keresztül. Ez egy rövidítés, amely leegyszerűsíti a mutatók által mutatott objektumok tagjainak elérését. Ha például ptr egy olyan objektumra mutat, amelynek van egy x tagváltozója, akkor a (*ptr).x helyett közvetlenül a ptr->x írható. Ez az operátor javítja a kód olvashatóságát és csökkenti a terjengősséget, amikor objektumokra mutató mutatókkal dolgozol.

Íme egy rövid példa: egy Superhero struct, amelynek van egy superpower tagváltozója. A main függvény létrehoz egy dianaPrince mutatót egy Superhero objektumra (amely Wonder Womant képviseli). A -> operátorral érjük el a superpower tagváltozót, bemutatva Wonder Woman ikonikus „Lasso of Truth” fegyverét.

struct Superhero {
    std::string superpower;
};
Superhero wonder_woman{};
Superhero* dianaPrince = &wonder_woman;
dianaPrince->superpower = "Lasso of Truth";
// Using the -> operator to access member variable superpower:
std::cout << "Wonder Woman, possesses the mighty " << dianaPrince->superpower;

Mutatók és referenciák

A mutatók és a referenciák egyaránt lehetővé teszik az objektumok közvetett elérését, de képességeikben és biztonsági szempontjaikban különböznek. A mutatók rugalmasan átirányíthatók más objektumra, és null értéket is kaphatnak. Ez a rugalmasság azonban kockázatokkal jár, például null mutatók visszafejtésével vagy lógó mutatók létrehozásával. A referenciák ezzel szemben nem lehetnek null értékűek, és létrehozásukkor érvényes objektumhoz kötődnek, így elkerülik ezeket a kockázatokat. Mivel biztonságosabbak, a referenciákat érdemes előnyben részesíteni a mutatókkal szemben, kivéve, ha szükség van a mutatók nyújtotta többletfunkciókra.

Utasítások

Üdvözlünk, mérnök! Te vagy a Speedywagon Alapítvány egyik utolsó veteránja, egy titkos szervezeté, amely évtizedek óta olyan ősi fenyegetések ellen harcol, mint a Pillar Men. Ennek során éveket töltöttél az Alapítvány technológiai rendszereinek karbantartásával, amelyek a legmodernebb technológia és elöregedett könyvtárak keverékéből épültek fel.

Az utóbbi időben azonban meghibásodnak a Pillar Men tevékenységét követő szenzorok. Az Alapítvány rendszerei régiek, és a kód egy olyan legacy C++ könyvtárral lép kapcsolatba, amelyet nem lehet frissíteni. A feladatod, hogy megvalósíts négy alapvető függvényt, amelyek egy régimódi, mutatóalapú könyvtár segítségével figyelik a Pillar Men szenzorok aktivitását.

Modern C++ mérnökként szívesebben használnál okos mutatókat, de sajnos a legacy kód tiszteletet követel a régi módszerek iránt. Az emberiség sorsa ezeken a mutatókon állhat vagy bukhat, szóval óvatosan haladj, és vezéreljen a Hamon-energia.

Note

Mivel a szenzorok mérései hatalmasak lehetnek, adunk egy minta struct-ot, amelyet a valódi könyvtár használ. A kódot már megvalósítottuk neked a fejlécfájlban.

struct pillar_men_sensor {
    int activity{};
    std::string location{};
    std::vector<int> data{};
};

1. A szenzorkapcsolat ellenőrzése (connection_check)

Az első feladatod, hogy megbizonyosodj róla, hogy a Pillar Men szenzor megfelelően csatlakozik. Nem engedhetjük meg, hogy leválasztott szenzorok téves riasztásokat váltsanak ki. Írj egy connection_check függvényt, amely egy nullptr-ellenőrzéssel megvizsgálja, hogy a szenzor mutatója érvényes-e.

  • Definiálj egy függvényt, amelynek paramétere egy pillar_men_sensor struct-ra mutató mutató.
  • A függvény true-t adjon vissza, ha a szenzor mutatója nem null, egyébként pedig false-t.
pillar_men_sensor* sensor{nullptr};
bool isConnected = connection_check(sensor);
// isConnected => false

2. A szenzorok aktivitásának megszámlálása (activity_counter)

A Pillar Men az árnyékban ólálkodnak, és tudnunk kell, hogy a szenzorok észleltek-e bármilyen aktivitást. Írd meg az activity_counter függvényt, amely egy szenzortömböt és egy kapacitást vár, utóbbi a tömbben lévő szenzorok számát adja meg.

  • Definiálj egy függvényt, amely egy tömb első elemére mutató mutatót és a tömb kapacitását várja.
  • Mutatóaritmetikával járd be a szenzortömböt, és gyűjtsd össze az aktivitásértékeket.
  • Add vissza az összegyűjtött aktivitást.
pillar_men_sensor sensor_array[3] = {{0}, {101}, {22}};
int totalActivity = activity_counter(sensor_array, 3);
// totalActivity => 123

3. Riasztásvezérlés (alarm_control)

Nem minden szenzor válthat ki riasztást, hacsak nincs valódi veszély. Az alarm_control függvény gondoskodik róla, hogy egy szenzor csak akkor váltson ki riasztást, ha az aktivitásszintje nagyobb 0-nál. A függvénynek emellett ellenőriznie kell a null szenzorokat is, hogy elkerülje a rendszer összeomlását.

  • Definiálj egy függvényt, amely elfogadja a pillar_men_sensor-ra mutató mutatót.
  • A függvény először ellenőrizze, hogy nullptr-e a szenzor. Ha a szenzor nullptr, adjon vissza false-t.
  • Ha a szenzor érvényes, és az aktivitása nagyobb 0-nál, adjon vissza true-t, egyébként false-t.
pillar_men_sensor db{9008, "songokunoie", {7, 7, 7}};
bool alarm = alarm_control(&db);
// alarm => true

4. Az adatok rendellenességeinek ellenőrzése a uv_alarm függvénnyel

Ebben a feladatban a uv_alarm függvényt valósítod meg, amely az UV-fénynek való kitettség és a szenzor aktivitása alapján dönti el, hogy ki kell-e váltani egy riasztást. A uv_alarm függvény használja a rendelkezésre álló uv_light_heuristic függvényt, amely egy adatvektoron dolgozik, és bizonyos küszöbértékek alapján ad vissza egy értéket. Ez egy minta verziója annak az összetett kódnak, amely éles környezetben fut majd, kérlek, ne változtasd meg az interfészt.

Definiáld a uv_alarm függvényt a speedywagon névtérben. A függvény:

  • vegyen át paraméterként egy pillar_men_sensor struct-ra mutató mutatót.
  • adjon vissza false-t, ha a szenzor mutatója null.
  • hívja meg a uv_light_heuristic függvényt, átadva a szenzor data tömbjének címét.
  • adjon vissza true-t, ha a uv_light_heuristic által visszaadott érték nagyobb, mint a sensor->activity szint, egyébként false-t.
Szerkesztés GitHubon A hivatkozás új ablakban vagy lapon nyílik meg
C++ Exercism

Készen állsz elkezdeni a(z) Speedywagon Alapítvány feladatot?

Iratkozz fel az Exercism-re, hogy megtanuld és elsajátítsd a(z) C++ nyelvet 19 fogalom100 feladat segítségével, valódi emberi mentorálással, mindez ingyen.