Эффективное использование STL - [49]

Шрифт
Интервал

>class Widget {

>public:

> Widget();

> Widget(double weight);

> Widget& operator=(double weight);

> …

>};

Предположим, мы хотим создать контейнер >map, ассоциирующий >int с >Widget, и инициализировать созданное множество конкретными значениями. Все выглядит очень просто:

>map m;

>m[1]=1.50;

>m[2]=3.67;

>m[3]=10.5;

>m[4]=45.8;

>m[5]=0.0003;

Настолько просто, что легко упустить, что же здесь, собственно, происходит. А это очень плохо, потому что в действительности происходящее может заметно ухудшить быстродействие программы.

Функция >operator[] контейнеров >map никак не связана с функциями >operator[] контейнеров >vector, >deque и >string, а также со встроенным оператором >[], работающим с массивами. Функция >map::operator[] упрощает операции «обновления с возможным созданием». Иначе говоря, при наличии объявления >map m команда >m[k]=v; проверяет, присутствует ли ключ >k в контейнере. Если ключ отсутствует, он добавляется вместе с ассоциированным значением >v. Если ключ уже присутствует, ассоциированное с ним значение заменяется на >v.

Для этого >operator[] возвращает ссылку на объект значения, ассоциированного с ключом >k, после чего v присваивается объекту, к которому относится эта ссылка. При обновлении значения, ассоциированного с существующим ключом, никаких затруднений не возникает — в контейнере уже имеется объект, ссылка на который возвращается функцией >operator[]. Но при отсутствии ключа >k готового объекта, на который можно было бы вернуть ссылку, не существует. В этом случае объект создается конструктором по умолчанию, после чего >operator[] возвращает ссылку на созданный объект.

Вернемся к началу исходного примера:

>map m;

>m[1]=1.50;

Выражение >m[1] представляет собой сокращенную запись для >m.operator[](1), поэтому во второй строке присутствует вызов >map::operator[]. Функция должна вернуть ссылку на ассоциированный объект >Widget. В данном примере >m еще не содержит ни одного элемента, поэтому элемент с ключом 1 не существует. Конструктор по умолчанию создает объект >Widget, ассоциируемый с ключом 1, и возвращает ссылку на этот объект. Наконец, созданному объекту >Widget присваивается значение 1.50.

Иначе говоря, команда

>m[1]=1.50:

функционально эквивалентна следующему фрагменту:

>typedef map intWidgetMap; // Вспомогательное определение типа


>pair result =       // Создание нового

> m.insert(intWidgetMap::value_type(1, Widget())); // элемента с ключом 1

>                                                  // и ассоциированным объектом, созданным

>                                                  // конструктором по умолчанию; комментарии

>                                                  // по поводу value_type

>                                                  // приведены далее

>result.first->second = 1.50; // Присваивание значения

>                             // созданному объекту

Теперь понятно, почему такой подход ухудшает быстродействие программы. Сначала мы конструируем объект >Widget, а затем немедленно присваиваем ему новое значение. Конечно, правильнее было бы сразу сконструировать >Widget с нужными данными вместо того, чтобы конструировать >Widget по умолчанию и затем выполнять присваивание. Следовательно, вызов >operator[] было бы правильнее заменить прямолинейным вызовом >insert:

>m.insert(intWidgetMap::value_type(1, 1.50));

С функциональной точки зрения эта конструкция эквивалентна фрагменту, приведенному выше, но она позволяет сэкономить три вызова функций: создание временного объекта >Widget конструктором по умолчанию, уничтожение этого временного объекта и оператор присваивания >Widget. Чем дороже обходятся эти вызовы, тем большую экономию обеспечивает применение >map::insert вместо >map::operator[].

В приведенном выше фрагменте используется определение типа >value_type, предоставляемое всеми стандартными контейнерами. Помните, что для >map и >multimap (а также для нестандартных контейнеров >hash_map и >hash_multimap — совет 25) тип элемента всегда представляет собой некую разновидность >pair.

Я уже упоминал о том, что >operator[] упрощает операции «обновления с возможным созданием». Теперь мы знаем, что при создании insert работает эффективнее, чем >operator[]. При обновлении, то есть при наличии эквивалентного ключа (см. совет 19) в контейнере >map, ситуация полностью меняется. Чтобы понять, почему это происходит, рассмотрим потенциальные варианты обновления:

>m[k] = v; // Значение, ассоциируемое

>          // с ключом k, заменяется на v при помощи оператора []

>m.insert(intWidgetMap::value_type(k, v)).first->second = v; // Значение, ассоциируемое

>                                                            // с ключом k, заменяется

>                                                            // на v при помощи insert

Вероятно, один внешний вид этих команд заставит вас выбрать >operator[], но в данном случае речь идет об эффективности, поэтому фактор наглядности не учитывается.

При вызове >insert передается аргумент типа >inWidgetMap::value_type (то есть >pair), потому при вызове >insert необходимо сконструировать и уничтожить объект данного типа. Следовательно, при вызове


Еще от автора Скотт Мейерс
Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14

Эффективный и современный С++Освоение С++11 и С++14 — это больше, чем просто ознакомление с вводимыми этими стандартами возможностями (например, объявлениями типов auto, семантикой перемещения, лямбда-выражениями или поддержкой многопоточности). Вопрос в том, как использовать их эффективно, чтобы создаваемые программы были корректны, эффективны и переносимы, а также чтобы их легко можно было сопровождать. Именно этим вопросам и посвящена данная книга, описывающая создание по-настоящему хорошего программного обеспечения с использованием C++11 и С++14 — т.е.


Как функции, не являющиеся методами, улучшают инкапсуляцию

Когда приходится инкапсулировать, то иногда лучше меньше, чем большеЯ начну со следующего утверждения: Если вы пишете функцию, которая может быть выполнена или как метод класса, или быть внешней по отношению к классу, Вы должны предпочесть ее реализацию без использования метода. Такое решение увеличивает инкапсуляцию класса. Когда Вы думаете об использовании инкапсуляции, Вы должны думать том, чтобы не использовать методы.Удивлены? Читайте дальше.


Рекомендуем почитать
Pro Git

Разработчику часто требуется много сторонних инструментов, чтобы создавать и поддерживать проект. Система Git — один из таких инструментов и используется для контроля промежуточных версий вашего приложения, позволяя вам исправлять ошибки, откатывать к старой версии, разрабатывать проект в команде и сливать его потом. В книге вы узнаете об основах работы с Git: установка, ключевые команды, gitHub и многое другое.В книге рассматриваются следующие темы:основы Git;ветвление в Git;Git на сервере;распределённый Git;GitHub;инструменты Git;настройка Git;Git и другие системы контроля версий.



DirectX 8. Начинаем работу с DirectX Graphics

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Симуляция частичной специализации

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Обработка событий в С++

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Питон — модули, пакеты, классы, экземпляры

Python - объектно-ориентированный язык сверхвысокого уровня. Python, в отличии от Java, не требует исключительно объектной ориентированности, но классы в Python так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.


SQL: быстрое погружение

Что общего между самыми востребованными профессиями и стремительным увеличением количества информации в мире? Ответ: язык структурированных запросов (SQL). SQL — рабочая лошадка среди языков программирования, основа основ для современного анализа и управления данными. Книга «SQL: быстрое погружение» идеальна для всех, кто ищет новые перспективы карьерного роста; для разработчиков, которые хотят расширить свои навыки и знания в программировании; для любого человека, даже без опыта, кто хочет воспользоваться возможностями будущего, в котором будут править данные.


Чистый код. Создание, анализ и рефакторинг

Даже плохой программный код может работать. Однако если код не является «чистым», это всегда будет мешать развитию проекта и компании-разработчика, отнимая значительные ресурсы на его поддержку и «укрощение». Эта книга посвящена хорошему программированию. Она полна реальных примеров кода. Мы будем рассматривать код с различных направлений: сверху вниз, снизу вверх и даже изнутри. Прочитав книгу, вы узнаете много нового о коде. Более того, вы научитесь отличать хороший код от плохого. Вы узнаете, как писать хороший код и как преобразовать плохой код в хороший. Книга состоит из трех частей.


Изучаем Python

Книга "Изучаем Python" - это ускоренный курс, который позволит вам сэкономить время и сразу начать писать работоспособные программы (игры, визуализации данных, веб-приложения и многое другое). Хотите стать программистом? В первой части книги вам предстоит узнать о базовых принципах программирования, познакомиться со списками, словарями, классами и циклами, вы научитесь создавать программы и тестировать код. Во второй части книги вы начнете использовать знания на практике, работая над тремя крупными проектами: создадите собственную "стрелялку" с нарастающей сложностью уровней, займетесь работой с большими наборами данных и освоите их визуализацию, и, наконец, создадите полноценное веб-приложение на базе Django, гарантирующее конфиденциальность пользовательской информации. Если вы решились разобраться в том что такое программирование, не нужно ждать.


Грокаем алгоритмы. Иллюстрированное пособие для программистов и любопытствующих

Алгоритмы - это всего лишь пошаговые алгоритмы решения задач, и большинство таких задач уже были кем-то решены, протестированы и проверены. Можно, конечно, погрузится в глубокую философию гениального Кнута, изучить многостраничные фолианты с доказательствами и обоснованиями, но хотите ли вы тратить на это свое время? Откройте великолепно иллюстрированную книгу и вы сразу поймете, что алгоритмы - это просто. А грокать алгоритмы - это веселое и увлекательное занятие.