Скотт Мейерс - Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14
- Название:Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14
- Автор:
- Жанр:
- Издательство:Вильямс
- Год:2016
- Город:Москва
- ISBN:978-5-8459-2000-3
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Скотт Мейерс - Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14 краткое содержание
В книге рассматриваются следующие темы. Освоение С++11 и С++14 — это больше, чем просто ознакомление с вводимыми этими стандартами возможностями (например, объявлениями типов
, семантикой перемещения, лямбда-выражениями или поддержкой многопоточности). Вопрос в том, как использовать их эффективно, чтобы создаваемые программы были корректны, эффективны и переносимы, а также чтобы их легко можно было сопровождать. Именно этим вопросам и посвящена данная книга, описывающая создание по-настоящему хорошего программного обеспечения с использованием C++11 и С++14 — т.е. с использованием современного С++.
■ Преимущества и недостатки инициализации с помощью фигурных скобок, спецификации
, прямой передачи и функций
интеллектуальных указателей
■ Связь между
,
, rvalue-ссылками и универсальными ссылками
■ Методы написания понятных, корректных,
лямбда-выражений
■ Чем
отличается от
, как они используются и как соотносятся с API параллельных вычислений С++
■ Какие из лучших методов “старого” программирования на С++ (т.е. С++98) должны быть пересмотрены при работе с современным С++
Более чем 20 лет книги
серии
являются критерием уровня книг по программированию на С++. Понятное пояснение сложного технического материала принесло ему всемирную известность. Он всегда самый желанный гость на международных конференциях, а его услуги консультанта широко востребованы во всем мире.
Скотт Мейерс Эффективный и современный С++, После изучения основ С++ я перешел к изучению того, как применять С++ в промышленном программировании, с помощью серии книг Скотта Мейерса Эффективный С++. Эффективный и современный С++ — наиболее важная из книг серии, предлагающая ключевые рекомендации, стили и идиомы, позволяющие эффективно использовать современный С++. Вы еще не купили эту книгу? Сделайте это прямо сейчас. Герб Саттер,
глава Комитета ISO по стандартизации С++, специалист в области архитектуры программного обеспечения на С++ в Microsoft
Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14 - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Функция 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>
), типы параметров функций в общем случае гарантируют, что между захватом ресурса (например, использованием оператора new
) и конструированием управляющего ресурсом объекта ничего не происходит. В функциях размещения прямая передача откладывает создание управляющих ресурсами объектов до тех пор, пока они не смогут быть сконструированы в памяти контейнера, и тем самым открывают окно, генерация исключения в котором может привести к утечке ресурсов. Все стандартные контейнеры подвержены этой проблеме. При работе с контейнерами, хранящими управляющие ресурсами объекты, вы должны принять меры, гарантирующие, что при выборе функции размещения вместо функции вставки вы не заплатите за повышение эффективности безопасностью исключений.
Откровенно говоря, вы в любом случае не должны передавать выражения наподобие “ 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::regexupperCaseWord("[A-Z]+");
Создание std::regex
из символьной строки может иметь достаточно высокую стоимость, так что для минимизации вероятности непреднамеренных расходов конструктор std::regex
, принимающий указатель const char*
, объявлен как explicit
. Именно поэтому не компилируются следующие строки:
std::regex r = nullptr; // Ошибка! Не компилируется!
regexes.push_back(nullptr); // Ошибка! Не компилируется!
В обоих случаях требуется неявное преобразование указателя в std::regex
, а объявление конструктора как explicit
его предотвращает.
Интервал:
Закладка: