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 azit
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: 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::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 << " ";
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!