Читаем Освой самостоятельно С++ за 21 день. полностью

57:    for (unsigned short i = 0; 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

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

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

123:       temp[i] = itsString[i];

124:    for (unsigned short j = 0; j

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

138:       temp[i] = itsString[i];

139:    for (unsigned short j = 0; j

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

Деструктор, выполняемый в строках 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 из полученного массива символов и передает его в функцию оператора присваивания. Такой процесс называется неявным преобразованием. Если бы в программе не был объявлен соответствующий конструктор, преобразующий массивы символов, то для этой строки компилятор показал бы сообщение об ошибке.

Перейти на страницу:

Похожие книги

Сущность технологии СОМ. Библиотека программиста
Сущность технологии СОМ. Библиотека программиста

В этой книге СОМ исследуется с точки зрения разработчика C++. Написанная ведущим специалистом по модели компонентных объектов СОМ, она раскрывает сущность СОМ, помогая разработчикам правильно понять не только методы модели программирования СОМ, но и ее основу. Понимание мотивов создания СОМ и ее аспектов, касающихся распределенных систем, чрезвычайно важно для тех разработчиков, которые желают пойти дальше простейших приложений СОМ и стать по-настоящему эффективными СОМ-программистами. Показывая, почему СОМ для распределенных систем (Distributed СОМ) работает именно так, а не иначе, Дон Бокс дает вам возможность применять эту модель творчески и эффективно для ежедневных задач программирования.

Дональд Бокс

Программирование, программы, базы данных / Программирование / Книги по IT