Главная страница Случайная страница КАТЕГОРИИ: АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника |
Листинг 12.12. Использование класса String
1: // Листинг. 12.12 2: 3: #include < iostream.h> 4: #include < string.h> 5: 6: // Рудиментарный класс string 7: class String 8: { 9: public: 10: // Конструкторы 11: String() 12: Stnng(const char *const), 13: Stnng(const String &), 14: ~Stnng() 15: 16: // Перегруженные операторы 17: char & operator[](unsigned short offset), 18: char operator[](unsigned short offset) const, 19: Stnng operator+(const String&), 20: void operator+=(const String&) 21: Stnng & operator= (const Stnng &), 22: 23: // Основные методы доступа 24: unsigned short GetLen()const { return itsLen, } 25: const char * GetStnng() const { return itsStnng, } 26: 27: private: 28: Stnng (unsigned short), // Закрытый конструктор 29: char * itsStnng, 30: unsigned short itsLen 31: } 32: 33: // Конструктор, заданный no умолчанию, создает строку нулевой длины 34: String String() 35: { 36: itsStnng = new char[1] 37: itsStrmg[0] = '\0' 38: itsLen=0; 39: } 40: 41: // Закрытый (вспомогательный) конструктор 42: // используется только методами класса для создания 43: // строк требуемой длины с нулевым наполнением 4й: String String(unsigned short len) 45: { 46: itsStnng = new char[len+1] 47: for (unsigned short i = 0 i< =len, i++) 48: itsString[i] = \0, 49: itsLen=len, 50: } 51: 52: // Преобразование массива символов в строку 53: String String(const char * const cString) 54: { 55: itsLen = strlen(cString); 56: itsString = new char[itsLen+1]; 57: for (unsigned short i = 0; i< itsLen: i++) 58: itsString[i] = cString[i]; 59: itsString[itsLen]='\0'; 60: } 61: 62: // Конструктор-копировщик 63: String:: String (const String & rhs) 64: { 65: itsLen=rhs.GetLen(); 66: itsString = new char[itsLen+1]; 67: for (unsigned short i = 0; i< itsLen; i++) 68: itsString[i] = rhs[i]; 69: itsString[itsLen] = '\0'; 70: } 71: 72: // Деструктор для освобождения памяти 73: String:: ~String () 74: { 75: delete [] itsString; 76: itsLen = 0; 77: } 78: 79: // Оператор присваивания освобождает память 80: // и копирует туда string и size 81: String& String:: operator=(const String & rhs) 82: { 83: if (this == & rhs) 84: return *this; 85: delete [] itsString; 86: itsLen=rhs.GetLen(); 87: itsString = new char[itsLen+1]; 88: for (unsigned short i = 0; i< itsLen; i++) 89: itsString[i] = rhs[i]; 90: itsString[itsLen] = '\0'; 91: return *this; 92: } 93: 94: //неконстантный оператор индексирования 95: // возвращает ссылку на символ так, что его 96: // можно изменить! 97: char & String:: operator[](unsigned short offset) 98: { 99: if (offset > itsLen) 100: return itsString[itsLen-1]; 101: else 102: return itsString[offset]; 103: } 104: 105: // константный оператор индексирования для использования 106: // с константными объектами (см. конструктор-копировщик!) 107: char String:: operator[](unsigned short offset) const 108: { 109: if (offset > itsLen) 110: return itsString[itsLen-1]; 111: else 112: return itsString[offset]; 113: } 114: 115: // создание новой строки путем добавления 116: // текущей строки к rhs 117: String String:: operator+(const String& rhs) 118: { 119: unsigned short totalLen = itsLen + rhs.GetLen(); 120: String temp(totalLen); 121: unsigned short i; 122: for (i= 0; i< itsLen; i++) 123: temp[i] = itsString[i]; 124: for (unsigned short j = 0; j< rhs.GetLen(); j++, i++) 125: temp[i] = rhs[j]; 126: temp[totalLen]='\0'; 127: return temp; 128: } 129: 130: // изменяет текущую строку и возвращает void 131: void String:: operator+=(const String& rhs) 132: { 133: unsigned short rhsLen = rhs.GetLen(); 134: unsigned short totalLen = itsLen + rhsLen; 135: String temp(totalLen); 136: unsigned short i; 137: for (i = 0; i< itsLen; i++) 138: temp[i] = itsString[i]; 139: for (unsigned short j = 0; j< rhs.GetLen(); j++, i++) 140: temp[i] = rhs[i-itsLen]; 141: temp[totalLen]='\0'; 142: *this = temp; 143: } 144: 145: int main() 146: { 147: String s1(" initial test"); 148: cout < < " S1: \t" < < s1.GetString() < < endl; 149: 150: char * temp = " Hello World"; 151: s1 = temp; 152: cout < < " S1: \t" < < s1.GetString() < < endl; 153: 154: char tempTwo[20]; 155: strcpy(tempTwo, "; nice to be here! "); 156: s1 += tempTwo; 157: cout < < " tempTwo: \t" < < tempTwo < < endl; 158: cout < < " S1: \t" < < s1.GetString() < < endl; 159: 160: cout < < " S1[4]: \t" < < s1[4] < < endl; 161: s1[4]='o'; 162: cout < < " S1: \t" < < s1.GetString() < < endl; 163: 164: cout < < " S1[999]: \t" < < s1[999] < < endl; 165: 166: String s2(" Another string"); 167: String s3; 168: s3 = s1+s2; 169: cout < < " S3: \t" < < s3.GetString() < < endl: 170: 171: String s4; 172: s4 = " Why does this work? "; 173: cout < < " S4: \t" < < s4.GetString() < < endl; 174: return 0; 175: }
Результат: S1: initial test S1: Hello world tempTwo:; nice to be here! S1: Hello world; nice to be here! S1[4]: o S1: Hello World; nice to be here! S1[999]:! S3: Hello World; nice to be here! Another string S4: Why does this work?
Анализ: В строках 7—31 объявляется простой класс String. В строках 11—13 объявляются конструктор по умолчанию, конструктор-копировщик и конструктор для приема существующей строки с концевым нулевым символом (стиль языка С). В классе String перегружаются операторы индексирования ([]), суммирования (+) и присваивания с суммой (+=). Оператор индексирования перегружается дважды. Один раз как константная функция, возвращающая значение типа char, а другой — как неконстантная функция, возвращающая указатель на char. Неконстантная версия оператора используется в выражениях вроде строки 161: SomeString[4]=V; В результате открывается прямой доступ к любому символу строки. Поскольку возвращается ссылка на символ, функция получает доступ к символу и может изменить его. Константная версия оператора используется в тех случаях, когда необходимо получить доступ к константному объекту класса String, например при выполнении конструктора-копировщика в строке 63. Обратите внимание, что в этом случае открывается доступ к rhs[i], хотя rhs был объявлен как const String &. К этому объекту невозможно получить доступ, используя неконстантные функции-члены. Поэтому оператор индексирования необходимо перегрузить как константный. Если возвращаемый объект окажется слишком большим, возможно, вам потребуется установить возврат не значения, а константной ссылки на объект. Но поскольку в нашем случае один символ занимает всего один байт, в этом нет необходимости. Конструктор, заданный по умолчанию, выполняется в строках 33—39. Он создает строку нулевой длины. Общепринято, что в классе String длина строки измеряется без учета концевого нулевого символа. Таким образом, строка, созданная по умолчанию, содержит только концевой нулевой символ. Конструктор-копировщик выполняется в строках 63—70. Он задает длину новой строки равной длине существующей строки плюс одна ячейка для концевого нулевого символа. Затем конструктор-копировщик копирует существующую строку в новую и добавляет в конце нулевой символ окончания строки. В строках 53—60 выполняется конструктор, принимающий строку с концевым нулевым символом. Этот конструктор подобен конструктору-копировщику. Длина существующей строки определяется с помощью стандартной функции strlen() из библиотеки String. В строке 28 объявляется еще один конструктор, String(unsigned short), как закрытая функция-член. Он был добавлен для того, чтобы не допустить создания в классе String строк произвольной длины каким-нибудь другим пользовательским классом. Этот конструктор позволяет создавать строки только внутри класса String в соответствии со сделанными установками, как, например, в строке 131 с помощью operator+=. Более подробно этот вопрос рассматривается ниже, при объявлении operator+=. Конструктор String(unsigned short) заполняет все элементы своего массива символов значениями NULL. Поэтому в цикле for выполняется проверка i< =len, а не i< len. Деструктор, выполняемый в строках 73—77, удаляет строку текста, поддерживаемую классом String. Обратите внимание, что за оператором delete следуют квадратные скобки. Если опустить их, то из памяти компьютера будут удалены не все объекты класса, а только первый из них. Оператор присваивания прежде всего определяет, не соответствуют ли друг другу операнды слева и справа. Если операнды отличаются друг от друга, то текущая строка удаляется, а новая копируется в эту область памяти. Чтобы упростить присвоение, возвращается ссылка на строку, как в следующем примере: String1 = String2 = String3; Оператор индексирования перегружается дважды. В обоих случаях проверяются границы массива. Если пользователь попытается возвратить значение из ячейки памяти, находяшейся за пределами массива, будет возвращен последний символ массива (len-1). В строках 117-128 оператор суммирования (+) выполняется как оператор конкатенации. Поэтому допускается создание новой строки из двух строк, как в следующем выражении: String3 = String1 + String2; Оператор (+) вычисляет длину новой строки и сохраняет ее во временной строке temp. Эта процедура вовлекает закрытый конструктор, который принимает целочисленный параметр и создает строку, заполненную значениями NULL. Нулевые значения затем замещаются символами двух строк. Первой копируется строка левого операнда (*this), после чего — строка правого операнда (rhs). Первый цикл for последовательно добавляет в новую строку символы левой строки'. Второй цикл for выполняет ту же операцию с правой строкой. Обратите внимание, что счетчик i продолжает отсчет символов новой строки после того, как счетчик j начинает отсчет символов строки rhs. Оператор суммирования возвращает временную строку temp как значение, которое присваивается строке слева от оператора присваивания (string1). Оператор += манипулирует с уже существующими строками, как в случае string1 += string2. В этом примере оператор += действует так же, как оператор суммирования, но значение временной строки temp присваивается не новой, а текущей строке (*this = temp), как в строке 142. Функция main() (строки 145—175) выполняется для проверки результатов работы данного класса. В строке 147 создается объект String с помощью конструктора, задающего строки в стиле языка С с концевым нулевым символом. Строка 148 выводит содержимое этого объекта с помощью функции доступа GetString(). В строке 150 создается еще одна строка текста в стиле языка С. В строке 151 тестируется перегруженный оператор присваивания, а строка 152 выводит результат. В строке 154 создается третья строка с концевым нулевым символом — tempTwo. В строке 155 с помощью функции strcpy() происходит заполнение буфера строкой символов nice to be here!. В строке 156 с помощью перегруженного оператора += осуществляется конкатенация строки tempTwo к существующей строке s1. Результат выводится на экран в строке 158. В строке 160 возвращается и выводится на экран пятый символ строки — s1. Затем в строке 161 этот символ замещается другим с помощью неконстантного оператора индексирования ([]). Результат выводится строкой 162, чтобы показать, что символ строки действительно изменился. В строке 164 делается попытка получить доступ к символу за пределами массива. Возвращается и выводится на печать последний символ строки, как и было предусмотрено при перегрузке оператора индексирования. В строках 166 и 167 создаются два дополнительных объекта String, и в строке 168 используется перегруженный оператор суммирования. Результат выводится строкой 169. В строке 171 создается еще один объект класса String — s4. В строке 172 используется оператор присваивания, а строка 173 выводит результат. Оператор присваивания перегружен таким образом, чтобы использовать константную ссылку класса String, объявленную в строке 21, но в данном случае в функцию передается строка с концевым нулевым символом. Разве это допустимо? Хотя компилятор, ожидая получить объект String, вместо этого получает массив символов, он автоматически проверяет возможность преобразования полученного значения в ожидаемую строку. В строке 12 объявляется конструктор, который создает объект String из массива символов. Компилятор создает временный объект String из полученного массива символов и передает его в функцию оператора присваивания. Такой процесс называется неявным преобразованием. Если бы в программе не был объявлен соответствующий конструктор, преобразующий массивы символов, то для этой строки компилятор показал бы сообщение об ошибке.
|