![]() |
![]() |
![]() |
Анимация
JavaScript
|
Главная Библионтека 5. Последовательность. Если все функции наподобие empty являются функциями-членами, то такое решение наиболее поел едовател ьн о, поскольку все похожие функции имеют один и тот же синтаксис вызова. Вряд ли приятно запоминать, что надо писать length(str), но str.size(). Этот аргумент может показаться убедительным, но только до тех пор, пока мы не заметим, что достаточно написать для всех имеющихся функций-членов соответствующие версии обычных функций, как непоследовательность оказывается только кажущейся. В частности, что получится, если мы добавим для функций-членов наподобие size их обычные версии, которые будут просто вызывать соответствующие функции-члены? Это приведет к унификации синтаксиса вызовов. Например, если мы напишем функцию size, которая не будет членом класса, будут одинаково корректны оба варианта вызовов - как size(str), так и str.sizeC), что избавит от необходимости запоминать, какие именно функции являются членами класса. В таком случае везде будет использоваться только синтаксис вызова обычной функции, и мы получим все преимущества простоты, послсдовательности и инкапсуляции. Кстати, имеются и другие технические и конструкторские причины для предпочтения синтаксиса обычных функций. Последовательное написание обычных функций, не являющихся членами, существенно облегчает написание шаблонов. Если шаблоны могут для всех типов использовать функции, не являющиеся членами (в отличие от ситуации, когда часть функций является членами, а часть - нет), то они могут избежать использования классов-характеристик (traits) для выяснения, какие именно функции являются членами, а какие - нет, чтобы код корректно работал в обоих случаях (см. более подробные примеры в [Suttert)2]). Другая причина заключается в том, что предоставляется возможность перегружать (бывшие) функции-члены и не члены. Если вы в ужасе отпрянете и будете этому сопротивляться - учтите, что, как показывает опыт, зачастую это Хорошая Идея, и некоторые члены комитета по стандартизации С++ считают, что в новый стандарт С++- (С++Ох) можно бы было добавить перегрузку функций-членов и не членов. (Эта возможность может быть отвергнута, но некоторые эксперты считают, что в целом это потенциально неплохая идея). Так что все возражения по поводу последовательности в де йстви те л ы i ости отпадают, поскольку написание всех, где только это возможно, функций как обычных функций, не являющихся членами, приводит только к большей последовательности. В следующей задаче мы рассмотрим еще несколько операций, и выясним, нельзя ли и их сделать обычными функциями, не являющимися друзьями класса. Некоторые из них в соответствии с требованиями стандарта должны быть членами классов контейнеров, но мы будем рассматривать не вопрос о том, что нам говорит стандарт, а скорее о том, как бы мы разрабатывали эти классы, имея возможность начать все с белого листа. Задача 39. Ослабленная монолитность. Часть 3: уменьшение std:: stri ng Сложность: 5 Продолжаем разработку диеты для быстрого похудения std::string... Вопрос для новичка 1. Может ли string:: resize быть функцисй-не членом? Обоснуйте свой ответ. Вопрос для профессионала 2. Проанализируйте следующие функции std::string и покажите, могут ли они быть преобразованы в обычные функции-не члены. Обоснуйте ваш ответ. а) Присваивание, а также +=/append/push back. б) insert. Решение Операции, которые могут не быть членами В этой задаче мы увидим, что все перечисленные далее функции .могут быть реализованы как обычные функции, не являющиеся друзьями класса. • resize (2) • assign (6) • +=(3) • append (6) • push back • insert (7 - все, кроме версии с тремя параметрами) Приступим. resize 1. Может ли string:: resize быть функцией-не членом? Обоснуйте свой ответ. Давайте посмотрим. void resize(size type n, charx с); void resizeCsize type n); Можно ли эти функции сделать обычными функциями, не являющимися друзьями класса? Конечно, можно, поскольку их можно реализовать с использованием открытого интерфейса basic string без потери эффективности. В самом деле, спецификации стандарта выражают обе функции resize через другие, которые мы уже рассмат-ривали. Например, реализовать их как обычные функции, не являющиеся друзьями класса, можно следующим образом. template<class charx, class traits, class Allocator> void resize(basic string<charT, traits, Allocator>& s, typename Allocator::sizetype n, charx с ) if( n > s.max si2e() ) throw 1ength error("wont fit"); if( n <= S.sizeO ) { basic string<charx, traits, Allocator> temp(s,0,n); s.swap( temp ); } else { s.append( n - s.sizeO, с ); template<class charT, class trai ts, class Allocator> void resize(basic string<charT, traits, Allocator>& s, typename Allocator::size type n ) resize( s, n, сНагтО ) ; Это - один из способов реализации функций resize, ие являющихся членами, которые столь же эффективны, как и функции-члены. assign и +=/append/push back 2. Проанализируйте следующие функции std::string и покажите, могут ли они быть преобразованы в обычные функции-не члены. Обоснуйте ваш ответ. а) Присваивание, а также +=/append/push back. Давайте начнем с присваивания. У нас есть шесть - посчитайте сами - шесть видов присваивания. К счастью, этот случай прост: большинство из них уже выражены друг посредством друга, и все они могут быть реализованы с использованием комбинации конструктора и оператора operateг=. Остаются operator-*--, append и push back. Что можно сказать о всех этих многочисленных операциях добавления? Только то, что их схожесть наталкивает на мысль, что членом класса, вероятно, достаточно сделать только одну из них. В конце концов, все они делают одну и ту же работу, даже если немного и разнятся в деталях ее выполнения - например, добавление символа в одном случае, строки в другом и диапазона итераторов в третьем. Все они могут быть реализованы без потери эффективности в качестве обычных функций, не являющихся друзьями класса. • Ясно, что оператор operator+= можно реализовать посредство.м append, поскольку это указано в стандарте С++. • Точно так же понятно, что пять из шести версий append могут быть обычными функциями, не являющимися друзьями класса, поскольку все они определены с использованием версии append с тремя параметрами, которая в свою очередь может быть реализована посредством insert. • Определение статуса push back требует немного больше усилий, поскольку его семантика определена не в пункте, посвященном классу basic string, а в разделе о требованиях к контейнерам, и здесь мы обнаруживаем, что а.push back(x) - это просто синоним для a.insert(a.endO ,х). "Минутку! -- может сказать кое-кто из читателей. - Стандарт С++ гласит, что операторы присваивания должны быть членами, а += - оператор присваивания!" И да, и нет. Не углубляясь в детали, можно сказать, что хотя оператор += и перечислен вместе с остальными операторами типа @= как оператор присваивания в фамма-тике С ++, членом класса должен быть только оператор присваивания operator=. Следовательно, следующий пример реализации оператора operator+= как функции-не члена представляет собой совершенно корректный исходный текст С++. template<class charT, class trai ts, class Anocator> basic strinq<charT, traits, Allocator>& operator+=(basic string<charT, traits, AllocatorxS s, const basic string<charT, traits, Allocator>& t){ return s.append( t ); tempiate<class charT, class trai ts, class a1locator> Задача 39. Ослабленная монолитность. Часть3: уменьшение std::string 255 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 |
![]() |