A három const
Czirkos Zoltán · 2019.02.27.
Mit jelent a const a tagfüggvényeknél? A paramétereknél, a visszatérési értéknél? Milyen programozási hibák ellen véd?
Adott az alábbi kódrészelet:
class A {
B const & fv(C const & y) const;
};
Van ebben három const
minősítő is. Vajon melyik mire vonatkozik? Melyik mit akadályoz meg?
Konkrétabb példa
Tekintsünk egy mátrix osztályt. A mátrix valós számokat tartalmaz:
3.14 | 17 | 6 |
4.5 | 1.2 | 93 |
-23 | 0 | 4.5 |
Indexeléskor meg kell adnunk a sor- és az oszlopindexeket is. Viszont az indexelő operátornak csak egy paramétere lehet. Ezért úgy döntünk, hogy a sor- és oszlopindexet betesszük egy objektumba. A mátrix 1. oszlopa 2. sorának eleme így érhető el:
std::cout << m[Index(1,2)];
Ez formailag tökéletesen megfelel a legfölső kódnak. Az osztályaink tehát így néznek ki (csak a lényeges részek):
struct Index {
int x, y;
};
class Matrix {
int szelesseg, magassag;
double* adat;
double const & operator[] (Index const & i) const {
return adat[i.y * szelesseg + i.x];
}
};
Kérdés tehát most: melyik const
mit csinál, milyen programozási hiba ellen véd?
Az alábbi kódrészlet foglalja össze a hibákat:
class Matrix {
/* 3 */ /* 1 */ /* 2 */
double const & operator[] (Index const & i) const {
i.x = 32324; /* 1 */
this->szelesseg = 6789; /* 2 */
}
};
Matrix const m;
m[Index(3,2)] = 3.14; /* 3 */
A paraméter konstans: operator[] (Index const & i)
Az i
paraméter konstanssága akadályozza azt meg, hogy a tagfüggvényben az i
-nek, vagy az i
bármelyik tagváltozójának értéket adjunk. Tehát ezek a sorok fordítási hibát eredményeznek:
class Matrix {
double const & operator[] (Index const & i) const {
i.x = 32324; /* hiba */
i = Index(3,4); /* hiba */
}
};
Ez a konstans azért kellett, hogy az indexelésnél, az m[Index(3,2)]
kifejezésben a temporális objektumot át tudjuk venni paraméterként. Ahhoz konstans referencia kell, vagy érték szerinti paraméterátvétel. (Az indexelő objektum egyébként olyan kicsi, két egész szám adattaggal, hogy jobb lenne érték szerint átvenni.)
A tagfüggvény konstans: operator[] (...) const
A tagfüggvény konstanssága miatt a this
egy Matrix const *
típusú pointer lesz. Emiatt a mátrix objektum adattagjait nem lehet változtatni. Az alábbi sorok fordítási hibát eredményeznek:
class Matrix {
double const & operator[] (Index const & i) const {
this->szelesseg = 6789; /* hiba */
this->data = NULL; /* hiba */
}
};
Ez jogos is, hiszen az indexelés által egy konstans mátrix nem változhat meg, nem lehet se szélesebb, se keskenyebb.
Fontos megjegyezni, hogy bár a data
pointer ilyenkor konstans, továbbra is double *
a típusa. Tehát a konstansság kiterjed magára a pointerre, mint adattagra, de már nem terjed ki a pointer által mutatott elemekre. Egy ilyen hibától a tagfüggvény konstanssága nem ment meg minket:
class Matrix {
double const & operator[] (Index const & i) const {
this->data[0] = 3.14; /* az ellen nem véd */
}
};
A visszatérési érték konstans: double const & operator[] (...)
A visszatérési érték referencia szerint mutat rá a mátrix objektum által kezelt tömb egy elemére. Ha a mátrix konstans, akkor a benne tárolt számoknak sem szabad változniuk. Ezt olyan módon tudja biztosítani a mátrix, hogy indexelés hatására konstans referenciát ad vissza a tárolt valós számra. Így az alábbi kód fordítási hibához vezet:
Matrix const m( /* ... */ );
m[Index(3,2)] = 3.14;
Mégpedig azért, mert az értékadás bal oldalán bár referencia van, de az a referencia egy konstans számra vonatkozik, aminek nem lehet értéket adni.