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

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

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

Откровенно говоря, вы в любом случае не должны передавать выражения наподобие “>new Widget” в функции >emplace_back и >push_back, как и в большинство любых других функций, поскольку, как поясняется в разделе 4.4, это ведет к возможным проблемам с безопасностью исключений, одну из которых мы только что рассмотрели. Для предотвращения неприятностей требуется, чтобы получение указателя от “>new Widget” и превращение его в управляющий ресурсом объект выполнялось в одной инструкции, а уже затем этот объект передавался как rvalue функции, которой вы хотели изначально передавать “>new Widget”. (Более детально этот подход рассматривается в разделе 4.4.) Таким образом, код, использующий >push_back, должен быть записан скорее как

>std::shared_ptr spw(new Widget,  // Создание Widget и

>                            killWidget); // передача его spw

>ptrs.push_back(std::move(spw));          // Добавление spw

>                                         // как rvalue

Версия с использованием >emplace_back аналогична:

>std::shared_ptr spw(new Widget, killWidget);

>ptrs.emplace_back(std::move(spw));

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

Вторым важным аспектом функций размещения является их взаимодействие с конструкторами, объявленными как >explicit. Предположим, что вы создаете контейнер объектов регулярных выражений С++11:

>std::vector regexes;

Отвлекшись на ссору ваших коллег о том, как часто следует проверять свой Facebook, вы случайно написали следующий, казалось бы, бессмысленный код:

>regexes.emplace_back(nullptr);

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

>std::regex r = nullptr;// Ошибка! Не компилируется!

компилятор отвергнет такой код. Интересно, что будет отвергнут и вызов >push_back вместо >emplace_back:

>regexes.push_back(nullptr); // Ошибка! Не компилируется!

Такое любопытное поведение поясняется тем, что объекты >std::regex могут быть построены из символьных строк. Это делает корректным такой полезный код, как

>std::regex upperCaseWord("[A-Z]+");

Создание >std::regex из символьной строки может иметь достаточно высокую стоимость, так что для минимизации вероятности непреднамеренных расходов конструктор >std::regex, принимающий указатель >const char*, объявлен как >explicit. Именно поэтому не компилируются следующие строки:

>std::regex r = nullptr;     // Ошибка! Не компилируется!

>regexes.push_back(nullptr); // Ошибка! Не компилируется!

В обоих случаях требуется неявное преобразование указателя в >std::regex, а объявление конструктора как >explicit его предотвращает.

Однако в вызове >emplace_back мы передаем не объект >std::regex, а аргументы конструктора объекта >std::regex. Это не рассматривается как запрос неявного преобразования. Компилятор трактует этот код так, как если бы вы написали

>std::regex r(nullptr); // Компилируется

Если лаконичный комментарий “Компилируется” кажется вам лишенным энтузиазма, то это хорошо, потому что, несмотря на компилируемость, данный код имеет неопределенное поведение. Конструктор >std::regex, принимающий указатель >const char*, требует, чтобы этот указатель был ненулевым, а nullptr подчеркнуто нарушает данное требование. Если вы напишете и скомпилируете такой код, лучшее, на что вы можете надеяться, — аварийное завершение программы во время выполнения. Если вы не такой счастливчик, то вам предстоит получить немалый опыт работы с отладчиком.

На минутку оставляя без внимания >push_back и >emplace_back, обратим внимание на то, что очень похожие синтаксисы инициализации дают совершенно разные результаты:

>std::regex r1 = nullptr; // Ошибка! Не компилируется

>std::regex r2


Еще от автора Скотт Мейерс
Эффективное использование 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 «Информационная технология. Процессы жизненного цикла программных средств» с целью учета специфики разработки и документирования программного обеспечения встроенных систем реального времени.