Const член функции - Ефективно използване на C

const char& оператор[](std::size_t позиция) const // оператор[] за

char& оператор[](std::size_t позиция) // оператор[] за

Функцията operator[] в класа TextBlock може да се използва по следния начин:

const TextBlockctb("Свят");

Между другото, константните обекти най-често се срещат в реални програми в резултат на преминаване чрез указател или препратка към константа. Примерът с ctb по-горе е доста изкуствен. Но ето един по-реалистичен за вас:

// към постоянен обект

Чрез претоварване на operator[] и създаване на различни версии с различни типове връщане, можете да третирате const и non-const TextBlock обекти по различен начин:

tb[0] = 'x'; // нормално - писмено

ctb[0] = 'x'; // грешка! – запис

Имайте предвид, че грешката тук е свързана само с типа на стойността, върната от operator[]; самото повикване на operator[] е наред. Причината за грешката е опит за присвояване на стойност на обект от тип const char&, защото това е типът, върнат от const версията на operator[].

Това е така, защото върнатата стойност на вграден тип не може да бъде модифицирана правилно. Дори и да беше законно, фактът, че C++ връща обекти по стойност (вижте точка 20), би означавал, че копието на tb.text[0] е модифицирано, а не самата стойност на tb.text[0]. Това едва ли е това, което очаквате.

Нека си дадем почивка и да пофилософстваме малко. Какво означава функция-член да бъде const? Има две широко използвани концепции: побитово постоянство (известно също като физическо постоянство) и логическо постоянство.

Привържениците на побитовата константност вярват, че функция член е константна тогава и само ако не променя никаквичленове с данни на обект (с изключение на статичните), тоест не променя нито един бит в обекта. Хубавото на побитовата константа е, че нейното нарушение е лесно за откриване: компилаторът просто търси присвоявания на членовете на класа. Всъщност побитовата константа е C++-дефинирана константа: функция член с модификатор const не може да модифицира нестатичните членове на данните на обекта, към който е извикана.

За съжаление много членски функции, които се държат далеч от постоянно, преминават побитовия тест. По-специално, функция член, която променя това, към което сочи указателят, често не се държи като функция const. Но ако само указател принадлежи към обекта, тогава функцията е формално побитово постоянна и компилаторът няма да има нищо против. Това може да доведе до неочаквано поведение. Да предположим например, че имаме клас като Text-Block, където данните се съхраняват в низове char * вместо в низове, тъй като това е необходимо за предаване на функции, написани на C, който не разбира какво представляват низовите обекти.

char& operator[](std::size_t position) const // лошо (но побитово

В този клас функцията operator[] е (неправилно!) декларирана като константна член-функция, въпреки че връща препратка към вътрешните данни на обекта (тази тема се обсъжда в точка 28). Нека оставим това настрана засега и отбележим, че изпълнението на operator[] не променя pText по никакъв начин. В резултат на това компилаторът тихо ще генерира код за функцията operator[]. В края на краищата, той наистина е побитова константа и това е всичко, което компилаторът може да провери. Но вижте какво се случва:

char &pc = &cctb[0]; // извикване на const operator[] за получаване

// указател към cctb данни

*pc = 'j'; // cctb вече имакоето означава "желе"

Със сигурност има нещо нередно в това да създадете константен обект с конкретна стойност, да извикате само константна членска функция върху него и въпреки това да промените стойността му!

Това ни води до концепцията за логическа константност. Привържениците на тази философия твърдят, че константните членски функции могат да променят някои битове от обекта, който ги е извикал, но само по начин, който потребителят не може да открие. Например вашият клас CTextBlock може да кешира дължината на текстовия блок при всяка заявка:

std::size_t length() const;

std::size_ttextLength; // последната изчислена стойност на дължината

bool дължина е валидна; // правилната ли е дължината в момента

std::size_t CtextBlock::length() const

textLength = std::strlen(pText); // грешка! Не може да бъде присвоено

> // lengthIsValid в константа

Тази реализация на length() разбира се не е битова константа, тъй като може да променя стойностите на членовете textLength и lengthlsValid. Но в същото време отстрани изглежда, че това не застрашава постоянството на обектите CTextBlock. Компилаторът обаче не е съгласен. Той настоява за побитово постоянство. Какво да правя?

Решението е просто: използвайте променливия модификатор. Той освобождава членовете на нестатичните данни от ограниченията за побитова константа:

std::size_t length() const;

променлив std::size_t textLength; // Тези членове на данни винаги могат да бъдат

променлива логическа дължинаIsValid; // модифициран, дори в const

std::size_t CtextBlock::length() const

textLength = std::strlen(pText); // Поръчай сега