Czyli praktyczne zastosowanie tego, co już wiemy o const ;)
Załóżmy że potrzebujemy przeciążyć operator indeksowania (czyli []) dla swojej klasy. Weźmy na warsztat prosty przykład, bez żadnych szablonów itd.:
class MyArray
{
public:
explicit MyArray(const int size) : arr_size(size)
{
p_arr = new double[arr_size];
}
~MyArray()
{
if (p_arr)
{
delete [] p_arr;
}
}
// i także konstruktor kopiujący i operator przypisania ...
private:
double* p_arr;
const int arr_size;
};
Chcemy móc korzystać z operatora [] do wyciągania elementów tablicy - dopisujemy więc w części publicznej klasy:
double operator[](const int idx)
{
if (idx >= 0 && idx < arr_size)
{
return p_arr[idx];
}
throw std::invalid_argument("idx");
}
Problem pojawia się kiedy chcemy wykorzystać naszą klasę w następującym kodzie:
MyArray m = MyArray(5);
m[0] = 3.14;
Bum, błąd kompilacji. Gdzie jest nasz błąd?
Cóż, zwracana wartość jest tylko lokalną kopią - return niejawnie wywołuje konstruktor kopiujący. Rozwiązaniem jest dodanie jednego ampersandu do nagłówka funkcji:
double& operator[](const int idx)
Super, też już zawsze działa.
Ale zaraz, właśnie napisaliśmy funkcję:
void f(const MyArray& m)
{
cout << m[0] << endl;
}
I znowu błąd kompilacji - nasz operator [] nie jest metodą const, więc nie może działać na argumencie typu const MyArray&. A przecież nie modyfikujemy naszej klasy, więc na szybciutko dopisujemy do nagłówka:
double& operator[](const int idx) const
Hmmm.... Ale przecież m[0] = 3.14; modyfikuje obiekt m, czyli w sumie to const jest mylące i niepoprawne (uwaga, kompilator to puści!). Jakie jest rozwiązanie? Potrzebujemy przecież i takiej i takiej wersji operatora []...
I tak właśnie napiszemy, dopisując drugą wersję operatora:
double& operator[](const int idx) const
{
if (idx >= 0 && idx < arr_size)
{
return p_arr[idx];
}
throw std::invalid_argument("idx");
}
Definicja funkcji jest identyczna, różnica jest tylko w const w nagłówku. OK, zobaczmy czy to zadziała:
void f(const MyArray& m)
{
m[0] = 3.14;
cout << m[0] << endl;
}
Nadal nie powoduje błędu kompilacji (a powinno!) - dlaczego? Ponieważ operator [] nawet w wersji const zwraca wynik typu double&. A że nie chcemy mieć możliwości modyfikowania wyniku, to dodajemy kolejne const:
const double& operator[](const int idx) const
I gotowe :) W całości wygląda to tak:
double& operator[](const int idx)
{
if (idx >= 0 && idx < arr_size)
{
return p_arr[idx];
}
throw std::invalid_argument("idx");
}
const double& operator[](const int idx) const
{
if (idx >= 0 && idx < arr_size)
{
return p_arr[idx];
}
throw std::invalid_argument("idx");
}
Więcej: http://bytes.com/topic/c/answers/654228-index-operator-overloading
środa, 8 września 2010
Subskrybuj:
Komentarze do posta (Atom)
Brak komentarzy:
Prześlij komentarz