Анимация
JavaScript
|
Главная Библионтека string s3( s.dataO, 5 ); s3 содержит "01234" stri ng s4( s.beginC), s.begi nC)+5 ); s4 содержит "01234" Вот и все, кофе допит, отдых закончен... Работы осталось очень немного: всего только два семейства - compare и *find*. compare в) compare Предпоследнее семейство функций - compare. В нем пять членов, и можно тривиально показать, что все они эффективно реализуются как обычные функции, не являющиеся друзьями класса. Как? В стандарте эти функции определены через функции basic string size и data (которые, как мы уже решили, являются членами класса), а также trai ts:: compare, которая и выполняет всю работу (более полую информацию о trai ts: : compare можно найти в [Josuttis99]). Это было сравнительно просто? Тогда давайте вместо сравнивания сложностей займемся их поиском... find г) Семейство find (find, find * и rfind) Увы, найти их не сложно. На закуску нам осталось семейство функций, по сравнению с которым восемь insert и десять replace - так, забавы для разминки. В классе basic string - не верите, считайте сами - 24 (да, двадцать четыре) варианта алгоритмов поиска. Все их можно разбить на шесть подсемейств, каждое из которых состоит ровно из четырех функций: • find. Прямой поиск первого вхождения строки или символа (str, s или с), начиная с позиции pos; • rfind. Обратный поиск первого вхождения строки или символа (str, s или с), начиная с позиции pos; • fi nd fi rst of. Прямой поиск первого вхождения любого из одного или нескольких символов (str, s или с) из списка, начиная с позиции pos, • find last of. Обратный поиск первого вхождения любого из одного или нескольких символов (str, s или с) из списка, начиная с позиции pos; • fi nd fi rst not of. Прямой поиск первого вхождения любого симюла, кроме одного или нескольких указанных символов (str, s или с), начиная с позиции pos; • fi nd .l ast not of. Обратный поиск первого вхождения любого символа, кроме одного или нескольких указанных симгюлов (str, s или с), начиная с позиции pos. Каждое подсемейство состоит из четырех членов: • "str, pos", где str содержит искомые символы (или символы, для которых осуществляется поиск несовпадения), а pos - начальная позиция в строке; • "ptr, pos, n", где ptr - указатель charx*, указывающий на буфер длины п, содержащий искомые символы (или символы, для которых осуществляется поиск несовпадения), а pos - начальная позиция в строке; • "ptr, pos", где ptr - указатель charX*, указывающий на буфер, завершающийся нулевым символом, который содержит искомые символы (или символы, для которых осуществляется поиск несовпадения), а pos - начальная позиция в строке; • "с, pos", где с - искомый символ (или символ, для которого осуществляется поиск несовпадения), а pos - начальная позиция в строке. Все эти функции могут быть эффективно реализованы как обычные функции, которые не являются друзьями класса (оставляю это читателям в качестве очередного упражнения). Добавлю еше одно замечание о поиске в строках. В де йствите л ьн ости, как вы могли заметить, кроме большой группы алгоритмов basic string: :*fincl*, стандарт С++ предоставляет пусть не столь многочисленную, но полнофункциональную группу алгоритмов std: :*find*, в частности: • std: rfind может выполнять те же действия, что и basic string: :find; • std: :find с использованием reverse iterator, a также std: :find end могут выполнять те же действия, что и basic string:: rfind; • std: :find fi rst of или std: :find с соответствуюшим предикатом могут выполнять те же действия, что и basic string: :find fi rst of; • std: :find fi rst of или std:: find с соответствующим предикатом, использующие reverse iterator, могут выполнять тс же действия, что и basi c st ring: :find last of; • std: :find с соответствующим предикатом может выполнять тс же действия, что и basic„string:: find first not of; • std: :find с соответствующим предикатом и использованием reverse, i terator может выполнять тс же действия, что и basi c stri ng:: find 1ast not of. Кроме того, эти алгоритмы более гибкие, так как работают не только со строками. По сути, все алгоритмы basic string: :*find* можно реализовать с использованием алгоритмов std::fi nd и std::find end, снабдив их при необходимости соответствующими предикатами и/или итераторами reverse iterator. Так что, может быть, можно просто обойтись без алгоритмов basi c„string: :*find* и посоветовать программистам использовать вместо них существующие алгоритмы std: :find*? Можно, но осторожно: несмотря на то, что таким образом можно эмулировать все алгоритмы basi c stri ng: :"find*, в ряде случаев использование для этой цели реализации по умолчанию std::find* может привести к значительной потере производительности. Три вида каждой функции find и rfind, которые выполняют поиск подстрок (а не отдельных символов), можно реализовать гораздо более эффективно, чем методом "в лоб", когда производится проверка каждой позиции и сравнивание подстрок для каждой из них. Есть хорошо известные алгоритмы, "на лету" строящие для поиска подстрок (или доказательства их отсутствия) конечные автоматы, и вполне возможно их применение в реализации функций поиска. Нельзя ли воспользоваться такой оптимизацией, предоставив перегрузку (не специализацию - см. задачу 7) std: :find*, которая работает с итераторами basi c st ring: : iterator? Да, но только если basi cstring:: i terator является типом класса, а не обычным указателем сИагт*. Причина в том, что если бы эго был обычный указатель, то специализация std: :find для него срабатывала бы для всех без исключения указателей этого типа - что, очевидно, не верно. Она должна срабатывать только для указателей, указывающих в буфер std:: stri ng. По этой причине нам определенно нужно выделить basic string::iterator в отдельный тип, легко отличимый от других итераторов и указателей. Тогда специализированная версия может быть специально оптимизирована для максимально эффективною поиска подстрок. Резюме Декомпозиция и инкапсуляция - определенно Хорошие Веши. В частности, лучше разделять алгоритмы и контейнеры, как это сделано в стандартной библиотеке. Общеизвестно, что basic string имеет слишком много функций-членов. На самом деле из 103 функций basic string только 32 действительно должны быть членами, а 71 можно без потери эффективности сделать обычными функциями, не являющимися друзьями класса. Кроме того, многие из них дуплицируют функциональность имеющихся стандартных алгоритмов, или представляют собой алгоритмы, область применения которых может оказаться более широкой, если отделить их от basic stnng, а не прятать в классе. Не повторяйте ошибок 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 |