Анимация
JavaScript
|
Главная Библионтека std: :sort(s.beginO , s.endO , ComparePai rlstDeref<lterln, i nt>0) ; for( int i=0; i < s.sizeO; ++i , ++out ) *out = s[i].second; Решение 3 демонстрирует пару альтернативных деталей - в нем используется шар для того, чтобы избежать отдельного этапа сортировки, и std::transformO вместо вручную написанного цикла, код сократился до 16 строк и стал существенно более повторно используемым, этой версии требуется больше памяти и, вероятно, ее производительность немного ниже, так что мне больше нравится решение 2. Этот код - пример поиска альтернативного решения задачи. namespace solutions { tempiate<class т> struct CompareDeref { bool operatorOС const т& a, const т& b ) const { return *a < *b; } tempiate<class т, class u> struct Pair2nd { const u& operatorO С const std::pair<T,u>& a ) const { return a.second; } tempiate<class iterin, class Iter0ut> void sort idxtbl(Iterm first, iterin last, Iterout out){ std::multimap<lterin, int, CompareDeref<IterIn> > v; for( int i=0; first != last; ++i, ++first ) v.insertC std::make pair( fi rst, i ) ); std: :transform(v.beginO , v.endO , out, pair2nd<lterin const,int>()); Тестовая часть кода осталась no сути неизменной, за исключением использования итератора для получения результата (вместо int*) и использования исходного массива непосредственно в качестве контейнера. #include <iostream> int main() { int ai[10] = { 15,12,13,14,18,11,10,17,16,19 }; std::cout « "#################" « std::endl; std::vector<int> aidxtbl( 10 ); для тестирования другого решения используйте соответствующее имя пространства имен solutions: :sort idxtbl ( ai, ai+10, aidxtbl .beginO ); for( int i=0; i <10; ++i ) std::cout « "i=" « i « ", aidxtbl[i]=" « aidxtbl[i] « ", ai [aidxtbl [i]]=" « ai [aidxtbl [il] « std: :endl; std::cout « "#################" « std::endl; Задача 35. Обобщенные обратные вызовы Сложность: 5 Отчасти привлекательность обобщенного кода состоит в возможности его использования во всех ситуациях, которые только можно представить. Каким образом можно стилистически улучшить представленное в цитируемой статье средство, и каким образом можно сделать его более полезным, чтобы это был действительно обобщенный широко используемый код? Вопрос для новичка 1. Какие качества целесообразны при разработке и написании обобщенных средств? Поясните свой ответ. Вопрос для профессионала 2. Показанный далее код представляет интересную и очень полезную идиому для оболочек функций обратного вызова. Более детальное описание вы можете найти в оригинальной статье [KalevOl]. Отрецензируйте предложенный код и определите: а) возможные стилистические улучшения дизайна для лучшего идиоматического использования С++; б) механические ограничения полезности данного средства. template < class т, void (t::*f)() > class callback public: Присваивание члену object callback(t& t) : obiect(t) {} Запуск функции обратного вызова void executeO {(object.*f)();} private: t& object; Решение Качества обобщенности I. Какие качества целесообразны при разработке и написании обобщенных средств? Поясните свой ответ. Обобщенный код прежде всего должен быть практичен и удобен в использовании. Это не значит, что он должен включать все возможные варианты использования, включая варку кофе и мытье посуды. Это всего лишь означает, что в отношении обобщенного кода следует стараться избегать как минимум трех вещей. /. Избегайте чрезмерного ограничения типов. Например, если вы пишете обобщенный контейнер, то вполне разумно потребовать, чтобы содержащиеся в нем элементы имели, скажем, копирующий конструктор и не генерирующий исключений деструктор. Но надо ли требовать конструктор по умолчанию или оператор присваивания? Многие полезные типы, которые пользователи могут захотеть хранить в вашем контейнере, не имеют конструкторов по умолчанию, и если наш контейнер их использует, то такой тип не может быть использован в качестве типа элементов контейнера. Согласитесь, это ие слишком обобщенно... (В качестве примера можно привести задачу 11 и се контекст из [SutterOO] (задача 2.4 в русском издании).) 2. Избегайте чрезмерного ограничения функциональности. Если вы пишете некоторое программное средство, которое делает X и Y, то что, если некий пользователь захочет сделать Z, и это Z ие слишком отличается от Y? Иногда вы будете хотеть сделать ваш код достаточно гибким для поддержки Z, иногда нет. Частью хорошего обобщенного дизайна является выбор путей и средств для настройки или расширения вашего кода. То, что это важно в обобщенном дизайне, не должно оказаться сюрпризом, поскольку тот же принцип применим и к дизайну класса в объектно-ориентированном програм-мировании. Дизайн, основанный на стратегии, представляет собой одну из нескольких важных методик, которые обеспечивают "подключаемое" поведение обобщенного кода. Примеры такого дизайна вы можете найти в нескольких главах в [AlexandrescuOl]; начать можно с глав, где описываются SmartPtr и singleton. Это приводит нас к следующему вопросу. 3. Избегайте чрезмерно монолитного дизайна. Этот важный вопрос не возникает непосредственно при рассмотрении приведенного примера, но сам по себе заслуживает столь пристального внимания, что ему посвящена даже не одна задача, а целая мини-серия - задачи с 37 по 40. Во всех трех пунктах рефреном звучит слово "чрезмерно". Это означает именно то, что я хотел сказать, - хорошее решение лежит между чрезмерно малой (синдром "я уверен, что никто не будет использовать этот код с чем-то, кроме char") и чрезмерно большой обобщенностью (нездоровые фантазии "А если кто-то захочет применить эту программу дисплея тостера на межпланетной станции?"). Правильное решение, как и истина, всегда где-то посредине. Разбор обобщенных обратных вызовов 2. Показанный далее код представляет интересную и очень полезную идиому для оболочек функций обратного вызова. Более детальное описание вы можете найти в оригинальной статье [KalevOl]. Этот код представлен ниже. template < class т, void (т::*F)() > class callback publi с: Присваивание члену object callback(т& t) : object(t) {} Запуск функции обратного вызова void execute О {(object.*F)();} private: T& object; Итак, сколько способов ошибиться есть в простом классе со всего двумя однострочными функциями-членами? Как оказывается, чрезмерная простота и является частью проблемы. Этот шаблон класса не должен быть тяжеловесным, но и такая легковесность -- это тоже перебор. Улучшение стиля Отрецензируйте предложенный код и определите: а) возможные стилистические улучшения дизайна для лучшего идиоматического использования С++. Сколько возможных улучшений вы обнаружили? Вот что имел в виду я. 4. Конструктор должен быть описан как explicit. Автор, вероятно, не намеревался обеспечить неявное преобразование Т в cal 1 Ьаск<т>. "Правильные" классы не 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 |