Приказивање постова са ознаком ц++. Прикажи све постове
Приказивање постова са ознаком ц++. Прикажи све постове

четвртак, 15. април 2010.

Занимљиво питање

Питање је стигло од корисника:

Зашто на powerpc платформи овај код завршава у бесконачној петљи:


int main()
{
   int bar = 0;
   char foo;

   for (foo = 7; foo >= 0; foo--) {
       bar++;
   }

   return bar;
}


Одговор:
зато што је на powerpc платформи char не-негативан целобројни тип (unsigned char)

уторак, 14. април 2009.

Assert

Волим да користим асерте. Корисни су као само документујући код али и као помоћ при развоју.

Асерти су провере истинитости одређеног израза. Уколико евалуација израза који се прослеђује макроу „assert” за резултат даје нулу, програм се прекида уз поруку о изразу, имену фајла и линији. Порука може да се разликује у зависности од компајлера и система на коме се програмира, али ће све мање-више имати одређену информацију о томе где се асерт десио.

Када се програм искомпајлира са дефинисаним макроом „NDEBUG”, асерти нестају (макро assert бива дефинисан као ((void)0) и избачен као линија без ефекта).

Током развоја, важно је користити програм компајлиран без овог макроа (односно са укљученим асертима).

Један тривијалан пример:


#include <assert.h>
#include <string.h>

int mojstrlen(const char * const p)
{
assert(p != NULL);

return strlen(p);
}


У примеру се види предност (и мана) овог приступа. Иако не можемо да забранимо кориснику функције да проследи NULL вредност, омогућујемо му да открије своју грешку у току развоја. Такође, само кратким погледом на функцију кориснику (програмеру) ће бити јасно да треба да обезбеди не-нул вредност.

Сама чињеница да асерти не постоје у коначном програму који се испоручује их чини адекватним само за оне случајеве који не смеју да се десе на терену, али истовремено их чине идеалним за време тестирања.

Рецимо да хоћемо да направимо робусну библиотеку и затим је дистрибуирамо корисницима од којих не можемо да очекујемо да знају да вредност показивача не сме да буде нула, онда асерт није довољан, а можда ни адекватан. У том случају би било примереније урадити нешто овако:


int mojstrlen(const char *const p)
{
if (p == NULL)
return 0;
return strlen(p);
}



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


int mojstrlen(const char * const p)
{
assert(p != NULL && !!"Pokazivac 'p' ne sme biti NULL");

return strlen(p);
}

недеља, 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".



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