Массивы нужно все-таки иметь в единственном экземпляре; они, очевидно, должны быть объявлены константными и проинициализированы один раз при объявлении. Мы можем это сделать, если объявим их глобальными. Однако такое решение совершенно неприемлемо с точки зрения инкапсуляции: глобальные переменные излишне доступны и их использование способствует появлению трудноуловимых ошибок. Массивы по сути своей являются (и должны быть) частью класса TString.
В языке С++ есть средство, позволяющее сделать именно то, что нам требуется: объявить массивы в единственном экземпляре, сделать их константными, проинициализировать при объявлении и тем не менее локализовать их в классе. Такие константные поля-массивы надо объявить в классе статическими (см. п. п. 9.4.2 в [1]):
static const char lower[59]; // маленькие буквы
static const char upper[59]; // БОЛЬШИЕ БУКВЫ
Инициализация (определение) статических полей выполняется вне класса: const char TString::lower[59] =
"abcdef ghi]'к1тпордгз1иуыху2абвгдежзийклмнопрстуфхцчшщъыьэюя"; const char TString::upper[59] =
мАВСОЕРСНиК1ММОР0К$ТиУЫХУ2АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЬЫЬЭЮЯп;
Этот оператор присваивания является определением статического члена класса (а в классе — только объявление). Такой оператор присваивания — единственная форма инициализации константных статических массивов (и любых других константных статических полей встроенного нецелочисленного типа). Обратите внимание, что слово static в операторе инициализации отсутствует — его там писать нельзя. Статические массивы, как и статические константы (и вообще любые статические поля), являются частью класса, создаются в единственном экземпляре при запуске программы (до создания любых объектов класса), поэтому их нельзя инициализировать в конструкторе.
ПРИМЕЧАНИЕ
Помимо статических полей, в классе можно объявлять и статические методы. Такие методы являются методами класса и могут вызываться до создания первого объекта этого класса.
Как мы помним, статические константы места в классе не занимают. Точно так же не увеличивают размер класса и любые статические поля — они хранятся вне класса. Но принцип инкапсуляции выполняется — доступ к таким полям имеют только методы и «друзья» класса. Наши методы перевода регистра упрощаются (листинг 4.20).
Листинг 4.20. Реализация методов перевода регистра
TString TString::operator++() // toUpper
{ for(int i = 0: i < this->Length(); i++)
{ for (int j = 0: j < 59: ]++) if (s[i] == lower[j]) s[i]=upper[j]; }
return *this;
}
TString TString::operator--() // toLower
{ for(int i = 0: i < this->Length(): i++)
{ for (int j = 0: j < 59: ]++) if (s[i] == upperfj]) s[i]=lower[j]; }
return *this;
Проверим работу методов.
TString t ("Мама мыла Милу"); std::cout << t << std::endl;
++t; std::cout << t << std::endl;
--t; std::cout << t << std::endl;
TString b = "abCDefGH"; std::cout << b << stdnendl;
++b; std::cout << b << stdnendl;
--b; std::cout << b << std::endl;
Если вы на работе много печатали и сломали свой мфу, то не расстраивайтесь ведь его всегда можно починить - ремонт мфу Xerox недорого тут.
При выполнении этого фрагмента на экране появятся следующие строки:
Мама мыла Милу МАМА МЫПА МИЛУ мама мыла милу abCDefGH ABCDEFGH abcdefgh
Как видим, все работает правильно. Таким образом, мы создали законченный, вполне работоспособный класс строк. |