246071-Либерти-Освой-самостоятельно-С-за-21-день (852741), страница 59
Текст из файла (страница 59)
Размермассива String2 задан как MaxLength+1. Дополнительный элемент потребовался для концевогонулевогосимволастроки,которыйдобавляетсяавтоматическиобеимифункциями—strcpy()иstrncpy().КлассыстрокМногиекомпиляторыC++содержатбиблиотекиклассов,спомощьюкоторыхможнорешатьразличные прикладные задачи. Одним из представителей встроенных классов является классString.Язык C++ унаследовал от С концевой нулевой символ окончания строки и библиотекустроковых функций, куда входит функция strcpy(). Но все эти функции нельзя использовать вобъектно-ориентированном программировании. Класс String предлагает набор встроенныхфункций-членов и переменных-членов, а также методов доступа, которые позволяютавтоматически решать многие задачи, связанные с обработкой текстовых строк, получаякомандыотпользователя.ЕсливвашемкомпиляторенетвстроенногоклассаString,аиногдаивтехслучаях,когдаонесть, бывает необходимо создать собственный класс работы со строками.
Далее в этой главерассматривается процедура создания и применения класса String и пользовательских классовработысостроками.Как минимум, класс String должен преодолеть ограничения, свойственные использованиюмассивов символов. Подобно другим массивам, массивы символов статичны. Вам приходитсязадавать их размер при объявлении или инициализации. Они всегда занимают все отведенноедлянихпространствопамяти,дажеесливыиспользуететолькопо-ловинуэлементовмассива.Записьданныхзапределымассиваведетккатастрофе.Хорошо написанный класс работы со строковыми данными выделяет столько памяти,сколько необходимо для текущего сеанса работы с программой, и всегда предусматриваетвозможностьдобавленияновыхданных.Еслисвыделениемдополнительнойпамятивозникнутпроблемы, предусмотрены элегантные пути их решения.
Первый пример использования классаStringпоказанвлистинге12.12.Листинг12.12.ИспользованиеклассаString1://Листинг.12.122:3:#include<iostream.h>4:#include<string.h>5:6://Рудиментарныйклассstring7:classString8:{9:public:10://Конструкторы11:String()12:Stnng(constchar*const),13:Stnng(constString&),14:~Stnng()15:16://Перегруженныеоператоры17:char&operator[](unsignedshortoffset),18:charoperator[](unsignedshortoffset)const,19:Stnngoperator+(constString&),20:voidoperator+=(constString&)21:Stnng&operator=(constStnng&),22:23://Основныеметодыдоступа24:unsignedshortGetLen()const{returnitsLen,}25:constchar*GetStnng()const{returnitsStnng,}26:27:private:28:Stnng(unsignedshort),//Закрытыйконструктор29:char*itsStnng,30:unsignedshortitsLen31:}32:33://Конструктор,заданныйnoумолчанию,создаетстрокунулевойдлины34:StringString()35:{36:itsStnng=newchar[1]37:itsStrmg[0]='\0'38:itsLen=0;39:}40:41://Закрытый(вспомогательный)конструктор42://используетсятолькометодамиклассадлясоздания43://строктребуемойдлиныснулевымнаполнением4й:StringString(unsignedshortlen)45:{46:itsStnng=newchar[len+1]47:for(unsignedshorti=0i<=len,i++)48:itsString[i]=\0,49:itsLen=len,50:}51:52://Преобразованиемассивасимволоввстроку53:StringString(constchar*constcString)54:{55:itsLen=strlen(cString);56:itsString=newchar[itsLen+1];57:for(unsignedshorti=0;i<itsLen:i++)58:itsString[i]=cString[i];59:itsString[itsLen]='\0';60:}61:62://Конструктор-копировщик63:String::String(constString&rhs)64:{65:itsLen=rhs.GetLen();66:itsString=newchar[itsLen+1];67:for(unsignedshorti=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иsize81:String&String::operator=(constString&rhs)82:{83:if(this==&rhs)84:return*this;85:delete[]itsString;86:itsLen=rhs.GetLen();87:itsString=newchar[itsLen+1];88:for(unsignedshorti=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[](unsignedshortoffset)98:{99:if(offset>itsLen)100:returnitsString[itsLen-1];101:else102:returnitsString[offset];103:}104:105://константныйоператориндексированиядляиспользования106://сконстантнымиобъектами(см.конструктор-копировщик!)107:charString::operator[](unsignedshortoffset)const108:{109:if(offset>itsLen)110:returnitsString[itsLen-1];111:else112:returnitsString[offset];113:}114:115://созданиеновойстрокипутемдобавления116://текущейстрокикrhs117:StringString::operator+(constString&rhs)118:{119:unsignedshorttotalLen=itsLen+rhs.GetLen();120:Stringtemp(totalLen);121:unsignedshorti;122:for(i=0;i<itsLen;i++)123:temp[i]=itsString[i];124:for(unsignedshortj=0;j<rhs.GetLen();j++,i++)125:temp[i]=rhs[j];126:temp[totalLen]='\0';127:returntemp;128:}129:130://изменяеттекущуюстрокуивозвращаетvoid131:voidString::operator+=(constString&rhs)132:{133:unsignedshortrhsLen=rhs.GetLen();134:unsignedshorttotalLen=itsLen+rhsLen;135:Stringtemp(totalLen);136:unsignedshorti;137:for(i=0;i<itsLen;i++)138:temp[i]=itsString[i];139:for(unsignedshortj=0;j<rhs.GetLen();j++,i++)140:temp[i]=rhs[i-itsLen];141:temp[totalLen]='\0';142:*this=temp;143:}144:145:intmain()146:{147:Strings1("initialtest");148:cout<<"S1:\t"<<s1.GetString()<<endl;149:150:char*temp="HelloWorld";151:s1=temp;152:cout<<"S1:\t"<<s1.GetString()<<endl;153:154:chartempTwo[20];155:strcpy(tempTwo,";nicetobehere!");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:Strings2("Anotherstring");167:Strings3;168:s3=s1+s2;169:cout<<"S3:\t"<<s3.GetString()<<endl:170:171:Strings4;172:s4="Whydoesthiswork?";173:cout<<"S4:\t"<<s4.GetString()<<endl;174:return0;175:}Результат:S1:initialtestS1:HelloworldtempTwo:;nicetobehere!S1:Helloworld;nicetobehere!S1[4]:oS1:HelloWorld;nicetobehere!S1[999]:!S3:HelloWorld;nicetobehere!AnotherstringS4:Whydoesthiswork?Анализ:Встроках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(unsignedshort),какзакрытаяфункциячлен. Он был добавлен для того, чтобы не допустить создания в классе 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).