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

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

• Маловероятно, что контейнер отвергает новое значение как дубликат. Это означает, что либо контейнер разрешает наличие дубликатов, либо большинство передаваемых значений уникальны. Это важно, поскольку для того, чтобы определить наличие дубликата, реализации размещения обычно создают узел с новым значением, а затем сравнивают его с имеющимися узлами контейнера. Если добавляемое значение в контейнере отсутствует, узел встраивается в контейнер. Однако, если такое значение уже есть в контейнере, размещение прерывается, а узел уничтожается, так что впустую расходуется стоимость создания и уничтожения объекта. Такие узлы для функций размещения создаются более часто, чем для функций вставки.

Приведенные вызовы, с которыми мы уже сталкивались в данном разделе, удовлетворяют всем перечисленным критериям. Они работают быстрее соответствующих вызовов >push_back.

>vs.emplace_back("xyzzy"); // Конструирует новое значение в конце

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

>                          // типа, хранимого в контейнере;

>                          // контейнер не отвергает дубликаты

>vs.emplace_back(50, 'x'); // То же самое

При принятии решения об использовании функций размещения стоит иметь в виду еще пару вопросов. Первый из них связан с управлением ресурсами. Предположим, что у вас есть контейнер с объектами >std::shared_ptr

>std::list> ptrs;

и вы хотите добавить >std::shared_ptr, который должен быть освобожден с помощью пользовательского удалителя (см. раздел 4.2). В разделе 4.4 поясняется, что по возможности вы должны использовать для создания >std::shared_ptr функцию >std::make_shared, но в нем же указано, что существуют ситуации, когда это невозможно. Одна из таких ситуаций — когда вы хотите указать пользовательский удалитель. В этом случае для получения обычного указателя, которым будет управлять интеллектуальный указатель >std::shared_ptr, вы должны непосредственно использовать оператор >new.

Если пользовательский удалитель представляет собой функцию

>void killWidget(Widget* pWidget);

то код, использующий функцию вставки, может выглядеть следующим образом:

>ptrs.push_back(std::shared_ptr(new Widget, killWidget));

Он может также принять и такой вид, означающий то же самое:

>ptrs.push_back({ new Widget, killWidget });

В любом случае перед вызовом >push_back будет создан временный объект >std::shared_ptr. Параметром >push_back является ссылка на >std::shared_ptr, так что должен быть объект, на который ссылается этот параметр.

Функция >emplace_back должна избегать создания временного объекта >std::shared_ptr, но в этом случае ценность временного объекта гораздо выше его стоимости. Рассмотрим следующую потенциальную последовательность событий.

1. В любом из приведенных выше вызовов конструируется временный объект >std::shared_ptr, хранящий простой указатель, являющийся результатом операции “>new Widget”. Назовем этот объект >temp.

2. Функция >push_back получает >temp по ссылке. В процессе выделения памяти для узла списка, который должен содержать копию >temp, генерируется исключение нехватки памяти.

3. При выходе исключения за пределы >push_back объект >temp уничтожается. Поскольку это единственный интеллектуальный указатель >std::shared_ptr, указывающий на управляемый им объект >Widget, он автоматически удаляет последний, в данном случае с помощью вызова >killWidget.

Несмотря на происшедшую генерацию исключения нет никаких утечек: >Widget, созданный с помощью “>new Widget” в вызове >push_back, освобождается деструктором объекта >std::shared_ptr, который был создан для управления им (объектом >temp). Все отлично.

Рассмотрим теперь, что произойдет при вызове >emplace_back вместо >push_back:

>ptrs.emplace_back(new Widget, killWidget);

1. Обычный указатель, являющийся результатом выполнения “>new Widget”, передается с помощью прямой передачи в точку внутри >emplace_back, где выделяется память для узла списка. При попытке выделения памяти генерируется исключение нехватки памяти.

2. При выходе исключения за пределы >emplace_back обычный указатель, который был единственным средством доступа к >Widget в динамической памяти, оказывается потерянным. Происходит утечка >Widget (и всех ресурсов, которыми владеет этот объект).

В этом сценарии все совсем не отлично, и класс >std::shared_ptr в этом не повинен. Та же самая проблема возникнет при использовании >std::unique_ptr с пользовательским удалителем. По существу, эффективность классов управления ресурсами, таких как >std::shared_ptr и >std::unique_ptr, основана на немедленной передаче ресурсов (таких, как обычные указатели, возвращаемые оператором >new) конструкторам управляющих ресурсами объектов. Тот факт, что функции наподобие >std::make_shared и >std::make_unique автоматизируют этот процесс, является одной из причин, по которым эти функции так важны.

В вызовах функций вставки контейнеров, хранящих управляющие ресурсами объекты (например, >std::list>), типы параметров функций в общем случае гарантируют, что между захватом ресурса (например, использованием оператора


Еще от автора Скотт Мейерс
Эффективное использование STL

В этой книге известный автор Скотт Мейерс раскрывает секреты настоящих мастеров, позволяющие добиться максимальной эффективности при работе с библиотекой STL.Во многих книгах описываются возможности STL, но только в этой рассказано о том, как работать с этой библиотекой. Каждый из 50 советов книги подкреплен анализом и убедительными примерами, поэтому читатель не только узнает, как решать ту или иную задачу, но и когда следует выбирать то или иное решение — и почему именно такое.


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

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


Рекомендуем почитать
Изучаем Java EE 7

Java Enterprise Edition (Java EE) остается одной из ведущих технологий и платформ на основе Java. Данная книга представляет собой логичное пошаговое руководство, в котором подробно описаны многие спецификации и эталонные реализации Java EE 7. Работа с ними продемонстрирована на практических примерах. В этом фундаментальном издании также используется новейшая версия инструмента GlassFish, предназначенного для развертывания и администрирования примеров кода. Книга написана ведущим специалистом по обработке запросов на спецификацию Java EE, членом наблюдательного совета организации Java Community Process (JCP)


Платформа J2Me

Эта книга научит вас, как разрабатывать программное обеспечение для платформы J2ME компании «Sun Microsystems». Эта книга придерживается стиля учебного пособия, это не справочное руководство.Цель — дать вам твердую основу в понятиях и техниках, которая даст вам возможность решиться на самостоятельную разработку качественных приложений.


Виртуальная библиотека Delphi

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


Обработка баз данных на Visual Basic.NET

Это практическое руководство разработчика программного обеспечения на Visual Basic .NET и ADO.NET, предназначенное для создания приложений баз данных на основе WinForms, Web-форм и Web-служб. В книге описываются практические способы решения задач доступа к данным, с которыми сталкиваются разработчики на Visual Basic .NET в своей повседневной деятельности. Книга начинается с основных сведений о создании баз данных, использовании языка структурированных запросов SQL и системы управления базами данных Microsoft SQL Server 2000.


Исчерпывающее руководство по написанию всплывающих подсказок

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


Программное обеспечение встроенных систем. Общие требования к разработке и документированию

Embedded system software. General requirements for development and documentationСтандарт подготовлен в развитие ГОСТ Р ИСО/МЭК 12207-99 «Информационная технология. Процессы жизненного цикла программных средств» с целью учета специфики разработки и документирования программного обеспечения встроенных систем реального времени.