Программирование >>  Обобщенные обратные вызовы 

1 ... 77 78 79 [ 80 ] 81 82 83 84


функций с использованием прочих уже рассмотренных функций-членов нет. Есть два семейства функций erase.

eraseC pos, length )

basi c string& erase(size type pos = 0, size type n = npos);

erase( iter, ... )

iterator erase(iterator position);

iterator erase(iterator first, iterator last);

Сразу обратим внимание на различия возвращаемых этими семействами типов: первая версия возвращает ссылку на строку, а остальные - итератор, указывающий на позицию, следующую непосредственно за удаленным символом или диапазоном символов. Далее заметим, что различны и типы аргументов этих двух семейств. Первая версия в качестве аргументов получает смещение и длину, в то время как вторая - итератор или диапазон, определяемый итераторами; к счастью, легко выполнить как преобразование итераторов в смешения (pos = i ter - beginO), так и смешений в итераторы (iter = begiп() + pos).

(Кстати: стандарт этого не требует, но разработчик вполне может реализовать basi c st ring так, что объекты этого типа будут хранить свои данные в памяти в непрерыв-ном массиве charT. Если это так, то очевидно, что преобразование итераторов в смещения и наоборот не приведут к каким-либо накладным расходам. Хочу, однако, заметить, что даже при использовании схемы сегментированного хранения строки можно разработать очень эффективный способ преобразования итераторов в смещения и обратно, используя только открытые интерфейсы контейнеров и итераторов. Это небольшое отступление от основной темы сделано исключительно для полноты изложения.)

Все сказанное позволяет нам выразить первые две версии функции через третью.

tempiate<class charT, class traits, class Allocater>

basic string<charT, traits, Allocator>&

erase(basic strinq<charT, traits, Allocator>& s,

typename Allocator::size.type pos = 0,

typename Allocator::size type n =

basic string<charT, traits, Allocator>::npos )

if( pos > S.sizeO )

throw out of range( yes, we have no bananas ); typename basic string<charT,traits,Al1ocator>::iterator

fi rst = s.begin()+pos,

last = n==basic string<charT,traits,Allocator>::npos ? S.endO : first + min( n, s.sizeO - pos ); if( fi rst != last )

s.erase( fi rst, last ); return s;

tempiate<class charT, class traits, class Allocator> typename basic strinq<charT, traits, Allocatoo::iterator erase(basic string<cnarT, traits, Allocator>& s, typename basic string<charT, traits,

Allocator>::iterator position )

return s.erase( position, position+1 );

Ну что же, думаю, кофе к этому времени выпит?... replace

2. Проанализируйте оставшиеся функции-члены std::string и покажите, могут ли они быть сделаны обычными функциями-не членами. Обоснуйте ваш ответ.

а) replace



Итак, 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, size type n2); basic string& replaceCsize type pos, size type nl, 3

const cnarT* s, size type n2); basic string& replace(size type pos, size type nl, 4

const с

larT* s);

basic string& replaceCsize 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& replace(iterator il, iterator i2, 7

const charT* s, size .type n); basic string& replace(iterator il, iterator i2, 8

const charT* s); basic string& replaceCiterator il, iterator i2, 9

size type n, charT c); tempiate<class inputlterator> 10

basic string& replaceCiterator il, iterator i2,

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 сору(charT* s, size type n, size type pos = 0) const;

Это константная функция, которая не изменяет строку. Вместо этого строковый объект копирует часть самого себя (до п символов, начиная с позиции pos) в целевой буфер s (заметьте, я специально не сказал - в С-строку ), который должен быть достаточно большим - если это окажется не так, то программа будет писать данные куда-то в непонятное место в памяти, что тут же затянет ее в болото Неопределенного Поведения. И, что самое веселое, функция basic string::сору не, повторяю - не добавляет нулевой объект в целевой буфер (именно поэтому он и не является С-строкой. Второй причиной является то, что тип сЬагт - совсем не обязательно char; эта функция копирует в буфер символы любого типа, из которых состоит строка). Отсутствие нулевого завершающего символа делает эту функцию достаточно опасной.



1 ... 77 78 79 [ 80 ] 81 82 83 84

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика