1. Emlékeztető

Adott az alábbi kód tömbje.

  • Írj függvénysablont, amely paraméterként a tömb elejére mutató pointert, és a tömb vége utánra mutató pointert vesz át (az első már nem létező elemre). Írja ki a függvény a tömb összes elemét szóközzel elválasztva, majd a végén kezdjen egy új sort!
  • Írj függvénysablont, amely szintén elejére és vége utánra mutató pointert vesz át, továbbá egy függvényt vagy funktort. Cserélje ki ez a tömb összes elemét arra az értékre, amit a függvény visszatérési értékként ad! Például ha ez a függvény a globális ::sqrt, a tömb összes eleme le lesz cserélve a négyzetgyökére; 1, 2, 100-ból 1, 1.414, 10 lesz.

Hívd meg a függvényeket a tömbre!

#include <iostream>
#include <cmath>

int main() {
    double szamok[5] = { 1, 2, 10, 2.25, 3 };

    listaz( /* ... */ );
    atalakit( /* ... */, ::sqrt);
    listaz( /* ... */ );
}

2. A vektor és az iterátor I.

Adott az alábbi dinamikus tömb osztály:

#include <iostream>

class vektor {
  private:
    size_t meret;
    double *adat;

  public:
    vektor(size_t kezdeti);
    ~vektor();
    vektor(vektor const & masik);
    double & operator[](size_t idx);
    double const & operator[](size_t idx) const;
};

vektor::vektor(size_t kezdeti) {
    meret = kezdeti;
    adat = new double[meret];
    for (size_t i = 0; i < meret; ++i)
        adat[i] = 0;
}

vektor::~vektor() {
    delete[] adat;
}

vektor::vektor(vektor const & masik) {
    meret = masik.meret;
    adat = new double[meret];
    for (size_t i = 0; i < meret; ++i)
        adat[i] = masik.adat[i];
}

double & vektor::operator[](size_t idx) {
    return adat[idx];
}

double const & vektor::operator[](size_t idx) const {
    return adat[idx];
}

int main() {
    vektor v(3);
    v[0] = 1.2;
}

Emlékezz vissza: ha van destruktor, akkor kell lennie értékadó operátornak is. Ismétlésképp írd meg ennek az értékadó operátorát! Ez pár perces feladat kell legyen.

A vektor nem szívesen engedne hozzáférést a belső adatszerkezetéhez (méret, pointer). Ezért érdemes egy külön osztályt csinálni, amelyből létrehozott objektumokkal a vektor adott eleme hivatkozható, és a következő elemre lehet lépni. Ez az ún. iterátor. Valahogy így:

vektor v(5);
/* ... */
vektoriterator it;
for (it = v.eleje(); it != v.vege(); it.kovetkezo())
    std::cout << it.elem() << " ";

Figyeld meg jól a fenti kódot!

  • A v.eleje() függvényhívással a vektor elejére (0. indexű elemére) mutató iterátort kapunk. Ez a vektor tagfüggvénye. A kapott iterátort bemásoljuk az it változóba.
  • A v.vege() hasonlóan működik, de a vektor vége utáni elemre hivatkozik (ez az 5. indexű elem lenne, ami már nem létezik). Ez is a vektor tagfüggvénye.
  • Az így kapott iterátort it-hez hasonlítjuk: it != v.vege(). Az összehasonlítás az iterátorok != operátorával történik, mert az operátor mindkét oldalán iterátor áll. Ezért kell amúgy a .vege() iterátornak a vektor vége utáni képzeletbeli elemre hivatkoznia: ha it == vege, akkor megáll a ciklus.
  • it.kovetkezo() – ez az iterátor tagfüggvénye, és ettől a következő elemre lép. A sok egymás utáni .kovetkezo() hívás hatására előbb-utóbb egy olyan állapothoz kell jutni, ami megegyezik a v.vege() által adottal.
  • it.elem() – szintén az iterátor tagfüggvénye, és visszaadja az aktuális elem (a vektorban tárolt számok) referenciáját.

Milyen adatot kell ehhez tárolni az iterátorban?

  • Az iterátornak tudnia kell, hogy melyik vektorra hivatkozik (mert lehet több vektor).
  • Hányadik elemnél tart.

Implementáld a vektor szükszéges tagfüggvényeit és az iterátort!

Ehhez szükség lehet ún. elődeklarációra, mivel a vektor hivatkozik az iterátorra (az eleje() és vege() tagfüggvényeinél), és az iterátor meg hivatkozik a vektorra (értelemszerűen). Ezt így kell csinálni:

class vektoriterator;   /* osztály elődeklarációja */

class vektor {
    /* ... */
};

class vektoriterator {
    /* ... */
};

3. A vektor és az iterátor II.

Ha végeztél a fentiekkel, végezd el a következő átalakításokat.

  • Iterátor nem csak vektorhoz, hanem láncolt listához, sztringhez, fához, bármihez létezhet, amiben elemek tárolódnak. Ezért érdemes inkább úgy megírni a kódot, hogy az iterátor osztály a vektor osztályon belül van definiálva. Ilyenkor vektor::iterator típusnévvel érhető el.
  • Az iterátornak C++-ban nem szokás elnevezni a tagfüggvényeit, hanem inkább operátorokat használunk.

Tekintsd mintának az alábbi kódot! Akkor vagy kész, ha ez működik.

vektor v(5);
v[0] = 3.14;
v[2] = 8.6;

vektor::iterator it;
for (it = v.begin(); it != v.end(); ++it)
    std::cout << *it << " ";

4. Iterátorok és pointerek

Vedd észre, hogy a fenti operátorok használatával az iterátorok tulajdonképp úgy viselkednek, mintha pointerek lennének. operator++ lép a következő elemre (mint tömbben a pointer), és operator* adja meg az adott elem referenciáját.

Próbáld ezt ki az első feladatban megírt listaz() és atalakit() függvényeden! Ha azon helyesek a sablonparaméterek és függvényparaméterek, akkor azok át tudják venni a vektorod iterátorait is, nem csak a tömbbe mutató pointereket. Ha esetleg nem működne, javítsd ki. Ezt a kódot kell működésre bírni:

vektor v(5);
v[0] = 3.14;
v[2] = 8.6;

kiir(v.begin(), v.end());
atalakit(v.begin(), v.end(), ::sqrt);
kiir(v.begin(), v.end());

5. Vektor sablonnal

Alakítsd sablonná a vektor osztályt! Ennek kell működnie:

vektor<double> v(5);
v[0] = 3.14;
v[2] = 8.6;
kiir(v.begin(), v.end());

vektor<int> v(3);
v[0] = 1;
v[2] = 2;
kiir(v.begin(), v.end());

Működik az így átalakított vektorod sztringekre is? Ha nem, javítsd ki!

vektor<std::string> v(5);
v[0] = "alma";
v[1] = "barack";

6. Preinkremens és posztinkremens operátorok

Az i++ és a ++i mást jelent, ezt tudod Prog1-ből. A pointereknél is mást jelent a kettő, ezért az iterátoroknál is mást kell jelentsen:

vektor<std::string> v(5);
v[0] = "alma";
v[1] = "barack";

vektor<std::string> it;
it = v.begin();
std::cout << *(++it);       // v[1] = barack

it = v.begin();
std::cout << *(it++);       // v[0] = alma

A preinkremens és a posztinkremens operátorok külön is definiálhatóak. Mivel a nevük megegyezik, a C++ úgy különbözteti meg a kettőt, hogy a posztinkremens (valami++) alaknál a függvénynek van egy dummy int paramétere:

class Valami {
    ... operator++();       /* ++v */
    ... operator++(int);    /* v++ */
};

Egészítsd ki az iterátorod, hogy létezzen mindkét operátora!

7. Láncolt lista

Írj egy láncolt lista osztályt sablonnal! Lehessen beszúrni elemet a lista elejére, és hozzáfűzni a végén! Csinálj iterátort a láncolt listához!