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( /* ... */ );
}
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 azitvá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: hait == 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 av.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 {
/* ... */
};
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::iteratortí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 << " ";
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());
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";
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!