Анимация
JavaScript
|
Главная Библионтека Итак, replace: по правде говоря, работать с десятью функциями-членами replace малоинтересно, скучно и утомительно. Как минимум одна из этих функций replace должна быть функцией-членом (или другом), поскольку не имеется способа эффективно выразить replace с использованием уже рассмотренных ранее функций-членов. В частности, обратите внимание, что невозможно эффективно реализовать replace путем использования erase, за которым следует insert (или в обратном порядке), поскольку оба эти способа требуют перестановки большего количества символов, чем просто replace, а кроме того, могут вызвать перераспределение буфера в памяти. Обратите внимание, что у нас есть два семейства функций replace. replaceC pos, length, ... ) basic string& replace(size type posl, size type nl, const basic string& str); 1 basic .string& replace(size type posl, size type nl, 2 const basic string& str, size type pos2, STze type n2); basic string& rep1ace(size type pos, size type nl, 3 const charT* s, size type n2); basic„string& replace(size type pos, size type nl, 4 const с larT* s); basic string& replace(size type pos, size type nl, 5 size type n2, charT c); replaceC iter, iter, ... ) basic string& replaceCiterator il, iterator i2, 6 const basic string& str); basic string& replaceCiterator il, iterator i2, 7 const charT* s, si ze type n); basic string& replaceCiterator il, iterator i2, 8 const charT* s); basic string& replaceCiterator il, iterator i2, 9 size type n, charT c); tempiate<class inputlterator> 10 basi с stri ng& replaceCiterator il, i terator i 2, Inputlterator jl, inputlterator j2); Ha этот раз типы возвращаемых значений у обоих семейств одинаковы, что не может не радовать. Однако типы аргументов, как и в случае функции erase, у разных семейств различны: одно семейство основано на смещении и длине, в то время как второе использует диапазоны, определяемые итераторами. Как и в случае функции erase, возможность преобразования итераторов в позиции и наоборот позволяет нам легко реализовать одно семейство функций при помоши другого. Когда мы решаем, какая именно версия функции должна быть сделана членом, мы хотим выбрать наиболее гибкую и фундаментальную 8ерсию(и) и реализовать остальные функции через нее. В данном случае имеется несколько ловушек, подстерегающих нас во время анализа функций для выбора той, которая должна стать функцией-членом. Рассмотрим сначала первое семейство функций. • Одна функция (№2)? Как известно, стандарт определяет все первое семейство через версию №2. К сожалению, некоторые из функций при этом требуют создания временных объектов basic string, так что данный выбор оказывается не лучшим способом решения нашей проблемы. Стандарт описывает наблюдаемое поведение функций, но это описание - не обязательно наилучший способ реализации этих функций. • Две функции (№3 и М5)? Можно заметить, что все функции первого семейства (кроме пятой) можно эффективно реализовать посредством третьей функции, однако для того, чтобы избежать излишнего создания временного строкового (или эквивалентного) объекта, пятая функция должна рассматриваться как особый случай, требующий, чтобы она была функцией-членом. Перейдем к рассмотрению второго семейства. • Одна функция (№ 6) ? Стандарт определяет все второе семейство через версию №б. К сожалению, в этом случае вновь некоторые из функций требуют создания временных объектов basic string, так что данный выбор оказывается не лучшим способом решения нашей проблемы для второго семейства. • Три функции (№ 7, №9, №10)? Можно заметить, что большинство функций из второго семейства можно эффективно реализовать посредством функции №7, за исключением функции №9 (по той же причине, по которой в первом семействе функция №5 оказалась вынесена в отдельный случай, а именно потому что в этом случае не имеется готового буфера с корректным содержимым) и функции № 10 (в которой нельзя считать, что итераторы являются указателями, более того, что это итераторы basic„string::iterators!). • Две функции (No 9, М10)! Отако, как можно видеть, все функции второго семейства, за исключением функции №9, могут быть эффективно реализованы посредством функции №10, включая функцию №7. В действительности, в предположении реализации с непрерывным размещением строки и возможности преобразования позиций и итераторов друг в друга (чем мы уже пользовались ранее), мы, вероятно, можем даже реализовать все функции первого семейства... Вот оно, искомое решение! Похоже, лучшее, что мы можем сделать, - это две функции-члены, которые позволяют нам сделать все остальные функции обычными функциями, не являющимися друзьями класса. Эти функции-члены - версия с аргументами "iter, iter, num, char" и шаблонная версия. Все остальные функции членами не являются. (Упражнение для читателя: для всех восьми оставшихся функций самостоятельно разработайте их эффективные реализации в виде обычных функций, не являющихся друзьями класса.) Обратите внимание, как функция №10 иллюстрирует мощь шаблонов - эта единственная функция может использоваться для реализации всех прочих функций без потери эффективности, кроме двух, у которых небольшая потеря эффективности вызвана созданием временного объекта basic string, содержащего n копий одного и того же символа). Сейчас самое время выпить еще одну чашечку кофе... Второй перерыв на кофе: сору и substr б) сору и substr Ах, это сору... Обратите внимание на то, что это необычный зверь и что его интерфейс не согласуется с алгоритмом std:: сору. Еще раз посмотрим на его сигнатуру: size type сору(charx* s, size type n, size type pos = 0) const; Это константная функция, которая не изменяет строку. Вместо этого строковый объект копирует часть самого себя (до п символов, начиная с позиции pos) в целевой буфер S (заметьте, я специально не сказал - "в С-строку"), который должен быть достагочно большим - если это окажется не так, то программа будет писать данные куда-то в непонятное место в памяти, что тут же затянет ее в болото Неопределенного Поведения. И, что самое веселое, функция basic string::сору не, повторяю - не добавляет нулевой объект в целевой буфер (именно поэтому он и не является С-строкой. Второй причиной является то, что тип charx - совсем не обязательно char; эта функция копирует в буфер символы любого типа, из которых состоит строка). Отсутствие нулевого завершающего символа делает эту функцию достаточно опасной. > Рекомендация Никогда не используйте функции, которые пишут в буфер, размер которого не проверяется (например strcpy, sprintf), или функции, которые не завершают нулевым символом С-строку (например strncpy, basic string: :сору). Они могут привести не только к аварийному завершению работы профаммы, но и представляют собой уфозу безопасности-системы - атака, основанная на переполнении буфера, продолжает оставаться наиболее популярной у хакеров и разработчиков вредоносных профашл. Более подробно этот вопрос поднимается в задачах 2 и 3. Все то, что делается при помощи функции сору, можно не менее просто, но гораздо более гибко сделать при помощи старого доброго алгоритма std: :сору. string s = "0123456789"; char* bufl = new char[5]; s.copyCbufl. 0, 5); buf содержит О, V , 2, 3, 4 copy (s. begin С) , s. beginO+5, bufl) ; buf содержит 0, 1, 2, 3, 4 int* buf2 = new int[5]; s.copy(buf2, 0, 5); Ошибка: первый параметр не char* copy (s. begin О , s. beginO+5, buf 2) ; все в порядке: buf2 содержит значения, соответствующие символам "О", 1, "2, 3, "4 (т.е. их ASCII-значения) Кстати, этот код заодно демонсфирует, как basic string::сору можно фиви-ально сделать обычной функцией, не являющейся другом класса. Проще всего сделать это при помощи алгоритма сору. Пусть это останется еще одни.м упражнением для читателя, надо только не забыть корректно обработать частный случай п == npos. Переведите дыхание и сделайте очередной глоток кофе. Вот еще одна простенькая функция: substr. Вспомним ее сигнатуру*"". basic string substrCsize type pos = О, size type n = npos) const; Замети.м, что substr легко реализовать как обычную функцию, не являющуюся другом класса, поскольку даже стандарт гласит, что она просто возвращает новый объект типа basic string, сконсфуированный как basic string<charT,traits,Anocator>(data()+pos,min(n,size()-pos)) Таким образом, создание новой Сфоки, являющейся подсфокой существующей, можно одинаково легко и просто выполнить как с применением функции substr, так и без нее, с использованием более обобщенного конструктора stri ng. string s = "0123456789"; string s2 = s.substrC 0. 5 ); s2 содержит "01234" Проницательные читатели могут заметить, что эта функция получает параметры в порядке "позиция, длина", в то время как только что рассмотренная функция сору получает такие же параметры в порядке "длина, позиция". Помимо эстетического несоответствия, это может оказаться просто опасным. Попытка запомнить, в каком порядке надо передавать параметры в ту или иную функцию, может привести пользователей basic string в полный ступор, тем более что тип этих параметров одинаков, так что компилятор пропустит неверный по сути код без замечаний, ну а дальше... На мой предвзятый взгляд, такие вещи больше всего напоминают мину-растяжку на лесной тропе, на взрывателе которой выбита надпись "Сделано комитетом по стандартизации С++". Впрочем, я отвлекся... 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 [ 81 ] 82 83 84 85 |