Как сделать свой класс string c
Класс String
Объявление строк. Конструкторы класса string
Объекты класса String объявляются как все прочие объекты простых типов - с явной или отложенной инициализацией, с явным или неявным вызовом конструктора класса. Чаще всего, при объявлении строковой переменной конструктор явно не вызывается, а инициализация задается строковой константой. Но у класса String достаточно много конструкторов. Они позволяют сконструировать строку из:
- символа, повторенного заданное число раз;
- массива символов char[] ;
- части массива символов.
Некоторым конструкторам в качестве параметра инициализации можно передать строку, заданную типом char* . Но все это небезопасно, и подобные примеры приводиться и обсуждаться не будут. Приведу примеры объявления строк с вызовом разных конструкторов:
Объект world создан без явного вызова конструктора, а объекты sssss , stryes , strye созданы разными конструкторами класса String .
Заметьте, не допускается явный вызов конструктора по умолчанию - конструктора без параметров. Нет также конструктора, которому в качестве аргумента можно передать обычную строковую константу. Соответствующие операторы в тексте закомментированы.
Операции над строками
Над строками определены следующие операции:
- присваивание ( = );
- две операции проверки эквивалентности ( == ) и ( != );
- конкатенация или сцепление строк ( + );
- взятие индекса ( [] ).
Начну с присваивания, имеющего важную особенность. Поскольку string - это ссылочный тип, то в результате присваивания создается ссылка на константную строку, хранимую в "куче". С одной и той же строковой константой в "куче" может быть связано несколько переменных строкового типа. Но эти переменные не являются псевдонимами - разными именами одного и того же объекта. Дело в том, что строковые константы в "куче" не изменяются (о неизменяемости строкового типа будем далее говорить подробно), поэтому когда одна из переменных получает новое значение, она связывается с новым константным объектом в "куче". Остальные переменные сохраняют свои связи. Для программиста это означает, что семантика присваивания строк аналогична семантике значимого присваивания.
В отличие от других ссылочных типов, операции, проверяющие эквивалентность, сравнивают значения строк, а не ссылки. Эти операции выполняются как над значимыми типами.
Бинарная операция " + " сцепляет две строки, приписывая вторую строку к хвосту первой.
Возможность взятия индекса при работе со строками отражает тот приятный факт, что строку можно рассматривать как массив и получать без труда каждый ее символ. Каждый символ строки имеет тип char , доступный только для чтения, но не для записи.
Вот пример, в котором над строками выполняются данные операции:
Строковые константы
- обычные константы, которые представляют строку символов, заключенную в кавычки;
- @-константы , заданные обычной константой c предшествующим знаком @ .
В обычных константах некоторые символы интерпретируются особым образом. Связано это прежде всего с тем, что необходимо уметь задавать в строке непечатаемые символы, такие, как, например, символ табуляции. Возникает необходимость задавать символы их кодом - в виде escape-последовательностей. Для всех этих целей используется комбинация символов, начинающаяся символом " \ " - обратная косая черта. Так, пары символов: " \n ", " \t ", " \\ ", " \ "" задают соответственно символ перехода на новую строку, символ табуляции, сам символ обратной косой черты, символ кавычки, вставляемый в строку, но не сигнализирующий о ее окончании. Комбинация " \xNNNN " задает символ, определяемый шестнадцатеричным кодом NNNN . Хотя такое решение возникающих проблем совершенно естественно, иногда возникают неудобства: например, при задании констант, определяющих путь к файлу, приходится каждый раз удваивать символ обратной косой черты. Это одна из причин, по которой появились @-константы .
В @-константах все символы трактуются в полном соответствии с их изображением. Поэтому путь к файлу лучше задавать @-константой . Единственная проблема в таких случаях: как задать символ кавычки, чтобы он не воспринимался как конец самой константы. Решением является удвоение символа. Вот соответствующие примеры:
Взгляните на результаты работы приведенных фрагментов кода, полученные при вызове процедур TestDeclStrings и TestOpers .
Задача такая:
Составить описание класса для работы со вектором строк (строки
произвольной длины), с операциями включения в вектор, удаления из
вектора элемента с заданным значением, удаления всего вектора или
конца вектора, начиная с заданного элемента.
Класс Vector я описала, но до него мне необходимо описать класс String, так как нельзя пользоваться соответствующими библиотеками. Помогите, пожалуйста.
Вот описание класса Vector,
Это добавление записи в вектор. Остальные методы дописать смогу на основе этого примера (лектор объяснял)
чего вы хотите от String?
Кстати, пример ужасен, как вы собираетесь его использовать?
Говорят, что в идеале используются библиотеки string и vector, но нам запретили этим пользоваться. Вот посмотрите, я набросала кое-что, можете ответить, что должно быть на месте "//?" ? Я перегрузила операции в Stroka, там нужно еще что-нибудь?
оператор присваивания, наверное.
вот тут вызываеца: new_v [size-1] = s;
вам же компилятор поди сообщает чего не хватает?
если при этом все работает как задумано - то проблем нет, верно? )
попробуйте использовать свою строку и вектор - если будет работать - радуйтесь, компилятор сообщит об ошибках - тоже, ведь нашли ответ на "там нужно еще что-нибудь?"
я набросала кое-что, можете ответить, что должно быть на месте "//?" |
и может быть мы придумаем что должно быть на месте "//?".
Пока что мне не понятно почему в функции delall (которая, судя по названию, должна лишь все удалить выделяется память. И форматирование кода очень не нравится.
вместо вопросительных знаков в конструкторе по умолчанию:
Эти функции нужны, чтобы решить поставленную задачу (добавить элемент, удалить элемент по значению, удалить элементы,начиная со значения, удалить все).
Я просто не знаю, вдруг еще что-то надо, а я еще main() не заполняла. функции вызывать надо. А у меня в классах ввод-вывод строк не заявлен нигде. Это нормально?
Ну давайте я за него поругаюсь.
1) В классе Stroka не объявлены конструктор копии и оператор присваивания. Не в меру инициативный компилятор, к сожалению, в таком случае создаст их за Вас и это будет простое побитовое копирование. В частности, конструктор копии задействуется при передаче аргумента по значению в operator==. Копия будет создана, а на выходе из функции вызовется деструктор, а в нём delete[]. короче, оригинал будет безнадёжно испорчен. Во-первых, объявите конструктор копии и оператор присваивания; во-вторых, желательно бы передавать в качестве аргумента не объект типа Stroka, а константную ссылку на него, если это возможно (const Stroka&).
2) В методе Vector :: del Вы выделяете массив new_v размера X, а потом обращаетесь к new_v[X]. Поздравляю, Вы только что уронили программу.
3) Там же Вы в цикле вызываете delete[] v. Да и сам цикл получается почти бесконечный.
А у меня в классах ввод-вывод строк не заявлен нигде. Это нормально? |
Зависит от определения термина "нормально". Поскольку единственная строка, которую можно создать с использованием Вашего кода - это пустая строка, то вывод, собственно, не очень и нужен.
БлогNot. Лекции по C/C++: строки класса string
Лекции по C/C++: строки класса string
В современном стандарте C++ определен класс с функциями и свойствами (переменными) для организации работы со строками (в классическом языке C строк как таковых нет, есть лишь массивы символов char ):
Для работы со строками также нужно подключить стандартный namespace :
В противном случае придётся везде указывать описатель класса std::string вместо string .
Ниже приводится пример программы, работающей со string (в старых си-совместимых компиляторах не работает!):
- инициализация массивом символов (строкой встроенного типа) или другим объектом типа string . Встроенный тип не обладает второй возможностью;
- копирование одной строки в другую. Для встроенного типа приходится использовать функцию strcpy() ;
- доступ к отдельным символам строки для чтения и записи. Во встроенном массиве для этого применяется операция взятия индекса или косвенная адресация с помощью указателя;
- сравнение двух строк на равенство. Для встроенного типа используются функции семейства strcmp() ;
- конкатенация (сцепление) двух строк, дающая результат либо как третью строку, либо вместо одной из исходных. Для встроенного типа применяется функция strcat() , однако чтобы получить результат в новой строке, необходимо последовательно задействовать функции strcpy() и strcat() , а также позаботиться о выделении памяти;
- встроенные средства определения длины строки (функции-члены класса size() и l ength() ). Узнать длину строки встроенного типа можно только вычислением с помощью функции strlen() ;
- возможность узнать, пуста ли строка.
Рассмотрим эти базовые возможности более подробно.
Инициализация строк при описании и длина строки (не включая завершающий нуль-терминатор):
Строка может быть задана и пустой:
Для проверки того, пуста ли строка, можно сравнить ее длину с 0:
или применить метод empty() , возвращающий true для пустой строки и false для непустой:
Третья форма создания строки инициализирует объект типа string другим объектом того же типа:
Строка st3 инициализируется строкой st . Как мы можем убедиться, что эти строки совпадают? Воспользуемся оператором сравнения ( == ):
Как скопировать одну строку в другую? С помощью обычной операции присваивания:
Для сцепления строк используется операция сложения ( + ) или операция сложения с присваиванием ( += ). Пусть даны две строки:
Мы можем получить третью строку, состоящую из конкатенации первых двух, таким образом:
Если же мы хотим добавить s2 в конец s1 , мы должны написать:
Операция сложения может сцеплять объекты класса string не только между собой, но и со строками встроенного типа. Можно переписать пример, приведенный выше, так, чтобы специальные символы и знаки препинания представлялись встроенным типом char * , а значимые слова – объектами класса string :
Подобные выражения работают потому, что компилятор "знает", как автоматически преобразовывать объекты встроенного типа в объекты класса string . Возможно и простое присваивание встроенной строки объекту string :
Обратное преобразование при этом не работает. Попытка выполнить следующую инициализацию строки встроенного типа вызовет ошибку компиляции:
Чтобы осуществить такое преобразование, необходимо явно вызвать функцию-член с названием c_str() ("строка Си"):
Функция c_str() возвращает указатель на символьный массив, содержащий строку объекта string в том виде, в каком она находилась бы во встроенном строковом типе. Ключевое слово const здесь предотвращает "опасную" в современных визуальных средах возможность непосредственной модификации содержимого объекта через указатель.
К отдельным символам объекта типа string , как и встроенного типа, можно обращаться с помощью операции взятия индекса. Вот, например, фрагмент кода, заменяющего все точки символами подчеркивания:
Но лучше читать документацию по C++ и пользоваться его возможностями. Например, предыдущее действие мы могли бы выполнить вызовом одной-единственной функции replace() :
Правда, здесь использован не метод replace класса string , а одноимённый алгоритм:
Поскольку объект string ведет себя как контейнер, к нему могут применяться и другие алгоритмы. Это позволяет решать задачи, не решаемые напрямую функциями класса string .
Задание символов в строке
назначает символы строке
Доступ к отдельным символам
получение указанного символа с проверкой выхода индекса за границы
получение указанного символа
получение первого символа
получение последнего символа
возвращает указатель на первый символ строки
возвращает немодифицируемый массив символов С , содержащий символы строки
Исходный код примеров из этой статьи можете скачать из нашего github-репозитория.
Допустимо объявление строковых переменных через ключевое слово var :
Для объединения строк используется оператор +:
При работе со String следует помнить, что при переопределении значения переменной создается новый экземпляр строковой переменной в памяти. Поэтому, если вам нужно собрать строку из большого количества составляющих, то использование оператора + не самый лучший вариант. В этом случае будет происходить перерасход памяти: при выполнении операции объединения с присваиванием для очень большого количества подстрок, приложение может аварийно завершиться из-за того, что сборщик мусора не будет успевать удалять неиспользуемые объекты, а новые будут продолжать появляться с большой скоростью. Для решения этой задачи используйте StringBuilder , о нем будет рассказано в конце этого урока.
Создание и инициализация объекта класса String
Для дословного представления строки, для того чтобы проигнорировать управляющие последовательности, используйте префикс @ перед значением. Сравните вывод следующей конструкции:
Если требуется подготовить строковое значение с использованием набора переменных, то можно воспользоваться статическим методом Format класса String , либо префиксом $ :
Можно явно вызвать конструктор типа c передачей в него параметров. Самый простой вариант – это передать строку:
В качестве параметра может выступать массив Char элементов:
Ещё вариант – это указать элемент типа char и количество раз, которое его нужно повторить:
Для создания строки также можно использовать указатели на Char* и SByte* , но в данном уроке эта тема рассматриваться не будет.
Базовый API для работы со строками
В рамках данного раздела рассмотрим наиболее интересные и полезные методы и свойства класса String .
Объединение строк. Оператор +, методы Concat и Join
Сцеплять строки между собой можно с помощью оператора + , при этом, в результате объединения, будет создан новый объект:
В составе API , который предоставляет System . String , есть метод Concat , который может выполнять ту же работу:
Метод Concat позволяет объединить до четырех строк через прямое перечисление. Если нужно таким образом объединить больше строковых переменных и значений, то используйте оператор +. Полезным свойством Concat является то, что он может принять на вход массив элементов типа String и объединить их:
Для объединения элементов с указанием разделителя используется метод Join . В предыдущем примере, элементы в массиве sArr1 уже содержали пробел, это не всегда удобно, решим задачу объединения элементов, которые не содержат разделителей, с помощью Join :
В качестве разделителя можно использовать любую строку:
Поиск и извлечение элементов из строки. Оператор [], методы IndexOf, IndexOfAny, LastIndexOf, LastIndexOfAny, Substring
Для получения символа из строки с конкретной позиции можно использовать синтаксис подобный тому, что применяется при работе с массивами – через квадратные скобки []:
Для решения обратной задачи: поиск индекса первого (последнего) вхождения элемента или сроки в данной строке используются методы IndexOf , IndexOfAny и LastIndexOf , LastIndexOfAny .
В таблице ниже перечислены некоторые из предоставляемых System . String вариантов этих методов.
IndexOf(Char)
Возвращает индекс первого вхождения символа.
IndexOf(Char, Int32)
Возвращает индекс первого вхождения символа начиная с заданной позиции.
IndexOf(Char, Int32, Int32)
Возвращает индекс первого вхождения символа начиная с заданной позиции, проверяется указанное количество элементов.
IndexOf(String)
IndexOf(String, Int32)
IndexOf(String, Int32, Int32)
Назначение методов совпадает с перечисленными выше, но поиск выполняется для строки.
IndexOfAny(Char[])
IndexOfAny(Char[], Int32)
IndexOfAny(Char[], Int32, Int32)
Назначение методов совпадает с перечисленными выше, но выполняется поиск индекса первого вхождения любого из переданных в массиве элементов.
Last IndexOf([Char | String])
Last IndexOf ( [Char | String], Int32)
Last IndexOf ( [Char | String], Int32, Int32)
Возвращает индекс последнего вхождения символа или строки. Можно задавать индекс, с которого начинать поиск и количество проверяемых позиций. [Char | String] – означает Char или String
LastIndexOfAny(Char[])
LastIndexOfAny(Char[], Int32)
LastIndexOfAny(Char[], Int32, Int32)
Возвращает индекс последнего вхождения любого из переданных в массиве элементов.Можно задавать индекс с которого начинать поиск и количество проверяемых позиций
Для определения того, содержит ли данная строка указанную подстроку, а также для проверки равенства начала или конца строки заданному значению используйте методы: Contains , StartsWith и EndsWith .
Contains(Char)
Contains(String)
Возвращает True если строка содержит указанный символ или подстроки.
StartsWith(Char)
StartsWith(String)
Возвращает True если строка начинается с заданного символа или подстроки.
EndsWith(Char)
EndsWith(String)
Возвращает True если строка заканчивается на заданный символ или подстроку.
Задачу извлечения подстроки из данной строки решает метод SubString :
Substring(Int32)
Возвращает подстроку начиная с указанной позиции и до конца исходной строки.
Substring(Int32, Int32)
Возвращает подстроку начиная с указанной позиции с заданной длины.
Сравнение срок
Для сравнения строк можно использовать оператор сравнения ==, при этом будут сравниваться значения строковых переменных, а не их ссылки, как это делается для других ссылочных типов.
Для сравнения также можно использовать метод Equals , но это менее удобный вариант:
Модификация срок
Класс String предоставляет довольно большое количество инструментов для изменения строк.
Вставка строки в исходную в заданную позицию осуществляется с помощью метода Insert :
Для приведения строки к заданной длине с выравниванием по левому (правому) краю с заполнением недостающих символов пробелами используются методы PadLeft и PadRight :
Метод Remove удаляет подстроку из исходной строки. Возможны два варианта использования:
Remove(Int32)
Удаляет все символы начиная с заданного и до конца строки.
Remove(Int32, Int32)
Удаляет с указанной позиции заданное число символов.
Замена элементов строки производится с помощью метода Replace . Наиболее часто используемые варианты – это замена символа на символ и строки на подстроку:
Для преобразования строки к верхнему регистру используйте метод ToUpper() , к нижнему – ToLower() :
За удаление начальных и конечных символов отвечают методы, начинающиеся на Trim (см. таблицу ниже).
Удаляет символы пробелы из начала и конца строки.
Удаляет экземпляры символа из начала и конца строки.
Удаляет экземпляры символов из начала и конца строки.
TrimStart()
TrimStart(Char)
TrimStart(Char[])
Удаляет экземпляры символов из начала строки.
TrimEnd()
TrimEnd(Char)
TrimEnd(Char[])
Удаляет экземпляры символов из конца строки.
Методы и свойства общего назначения
Рассмотрим некоторые из полезных методов и свойств, которые не вошли в приведенные выше группы.
System.Length – возвращает длину строки:
System.Split() – разделяет заданную строку на подстроки, в качестве разделителя используется указанный через параметр символ (или группа символов):
System.Empty – возвращает пустую строку.
Форматирование строк
Под форматированием строк, в рамках данного раздела, понимается встраивание в строку различных элементом (число, дата и т.п.), представленных в заданном формате. Форматирование можно осуществлять с помощью метода ToString с передачей в него нужных описателей, метода Format , который, в качестве аргументов, получает строку со специальными вставками, определяющими представление элементов и непосредственно сами элементы.
Для начала рассмотрим на нескольких примерах работу с этими методоми:
Каждый элемент форматирования представляется следующим образом:
где index – это индекс элемента, которым будет замещена данная конструкция;
alignment – выравнивание;
formatString – формат.
Ниже приведены примеры использования элементов форматирования:
Представление чисел
Для представления чисел используются следующие описатели формата (список не полный, более детальную информацию можете найти в официальной документации):
Описатель формата
“C” или “c”
“D” или “d”
Представление целого числа.
“E” или “e”
Представление числа в экспоненциальном виде.
“F” или “f”
Представление числа в формате с плавающей точкой.
“P” или “p”
Представление процентов, выводит число умноженное на 100 со знаком процента.
Читайте также: