недеља, 22. март 2009.

конст коректност

Kључна реч "const" у језицима це и це плус плус.

У пројектима често срећем код у којима се ова кључна реч не користи или се не користи доследно. То је штета јер доприноси не само читљивости него и преношењу идеје о томе шта се дешава у одговарајућој функцији ономе који је чита, док компајлер обезбеђује да коректност буде примењена.

Уместо много речи, ево неколико примера. Рецимо да имамо функцију која треба да као аргумент прими показивач на низ карактера. За пример приказујем три функције од којих свака као аргумент има показивач на низ карактера:

/* Deklaracije. */
int f1(char *tekst);
int f2(const char *tekst);
int f3(char const *tekst);
int f4(const char *const tekst);
int f5(char *const tekst);

У чему је разлика између ових декларација? Наравно, у коришћењу речи конст.

Прва нам каже да ће низ на који показује формални параметар "tekst" можда бити мењан у функцији; друга функција нам каже да низ на који показује текст сигурно неће бити мењан унутар функције (али не говори ништа о томе да ли ће вредност аргумента "tekst" бити мењана, што са тачке гледишта корисника функције није ни битно).

Трећа функција има идентично значенје као и функција "f2". Неко више воли такав запис, неко као у другој функији - све једно је. 

Четврта функција поред тога што "обећава" да неће мењати низ на који показује формални аргумент, још казује да неће мењати ни вредност тог аргумента. Са тачке гледишта корисника функције, ово додатно конст је ирелевантно - позивној функцији је свеједно шта ће се са аргументом десити. Иако није неправилна, ова декларација можда мало непотребно оптерећује декларацију и ја је обично не користим. 

Пета функција је дата само ради комплетности, ни овакву декларацију функције не користим јер, као што сам раније рекао, говори о нечему што онога ко користи функцију не занима. Семантички, идентична је функцији "f1", дакле низ на који показује аргумент може да буде мењан унутар функције.

Слично је са декларацијама променљивих, само што овде често има смисла користити и варијанте "char *const tekst;" и "const char * const tekst" у зависности од контекста.

У це-плус-плусу постоји и коршћење речи конст у својству модификатора метода класе. Ево примера:

/* Jednostavan primer const kao
modifikatora metoda. */
#include <string>
#include <iostream>

using std::string;
using std::cout;
using std::endl;

class Zaposleni
{
public:
Zaposleni(const string &ime)
: _ime(ime) {}

virtual ~Zaposleni() {}

string ime()
{
cout << "Pozvana ne-konst metoda." << endl;
return _ime;
}

string ime() const
{
cout << "Pozvana konst metoda." << endl;
return _ime;
}

private:
string _ime;
};

void stampajZaposleni(const Zaposleni &zap)
{
cout << "U funkciji stampajZaposleni" << endl;
cout << zap.ime() << endl;
}

int main(int argc, char* argv[])
{
Zaposleni zap("Mika");
cout << "Poziv preko konst deklarisane promenljive." << endl;
stampajZaposleni(zap);
cout << "Sada poziv preko ne-konst deklarisane promenljive: " << endl;
cout << zap.ime() << endl;
return 0;
}

Пример је тривијалан, али има једну интересантну тачку: који од два метода за име ће бити позван у функцији stampajZaposleni? Наравно, биће позване методе које су означене са конст јер смо и формални аргумент "zap" у функцији stampajZaposleni означили са конст у декларацији функције. Уколико искоментаришемо конст декларације метода "ime", откирћемо да програм више не може да се искомпајлира јер не-конст метода "ime" не "обећава" да неће мењати унутрашње стање објекта.

За разлику од тога, у самој функцији "main" ће бити позвана не-конст метода јер је таква и декларација локалне променљиве "zap".



Закључак: конст коректност није претерано компликована за разумевање. Па ипак, уколико се добро не разуме и не користи правилно, брзо доводи до пројеката чија се конст коректност више не може поправити. Зато је важно од самог почетка пројекта правилно користити ову интересанту и веома корисну особину програмског језика.