Скотт Мейерс - Эффективный и современный С++. 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 - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
• Освобождение ресурсов по умолчанию выполняется с помощью оператора delete
, однако поддерживаются и пользовательские удалители. Тип удалителя не влияет на тип указателя std::shared_ptr
.
• Избегайте создания указателей std::shared_ptr
из переменных, тип которых — обычный встроенный указатель.
4.3. Используйте std::weak_ptr
для std::shared_ptr
-подобных указателей, которые могут быть висячими
Парадоксально, но может быть удобно иметь интеллектуальный указатель, работающий как std::shared_ptr
(см. раздел 4.2), но который не участвует в совместном владении ресурсом, на который указывает (другими словами, указатель наподобие std::shared_ptr
, который не влияет на счетчик ссылок объекта). Эта разновидность интеллектуального указателя должна бороться с проблемой, неизвестной указателям std::shared_ptr
: возможностью того, что объект, на который он указывает, был уничтожен. Истинный интеллектуальный указатель в состоянии справиться с этой проблемой, отслеживая, когда он становится висячим , т.e. когда объект, на который он должен указывать, больше не существует. Именно таковым и является интеллектуальный указатель std::weak_ptr
.
Вы можете удивиться, зачем может быть нужен указатель std::weak_ptr
. Вы, вероятно, удивитесь еще больше, когда познакомитесь с его API. Указатель std::weak_ptr
не может быть ни разыменован, ни проверен на “нулевость”. Дело в том, что std::weak_ptr
не является автономным интеллектуальным указателем. Это — дополнение к std::shared_ptr
.
Их взаимосвязь начинается с самого рождения: указатели std::weak_ptr
обычно создаются из указателей s td::shared_ptr
. Они указывают на то же место, что и инициализирующие их указатели std::shared_ptr
, но не влияют на счетчики ссылок объекта, на который указывают:
auto spw = // После создания spw счетчик
std::make_shared(); // ссылок указываемого Widget
// равен 1. (О std::make_shared
// см. Раздел 4.4.)
…
std::weak_ptrwpw(spw); // wpw указывает на тот же
// Widget, что и spw. Счетчик
// ссылок остается равным 1
…
spw = nullptr; // Счетчик ссылок равен 0, и
// Widget уничтожается.
// wpw становится висячим
О висячем std::weak_ptr
говорят, что он просрочен (expired). Вы можете проверить это непосредственно:
if (wpw. expired()) … // Если wpw не указывает на объект…
Но чаше всего вам надо не просто проверить, не просрочен ли указатель std::weak_ptr
, но и, если он не просрочен (т.e. не является висячим), обратиться к объекту, на который он указывает. Это проще сказать, чем сделать. Поскольку у указателей std::weak_ptr
нет операций разыменования, нет и способа написать такой код. Даже если бы он был, разделение проверки и разыменования могло бы привести к состоянию гонки: между вызовом expired
и разыменованием другой поток мог бы переприсвоить или уничтожить последний std::shared_ptr
, указывающий на объект, тем самым приводя к уничтожению самого объекта. В этом случае ваше разыменование привело бы к неопределенному поведению.
Что вам нужно — так это атомарная операция, которая проверяла бы просроченность указателя std::weak_ptr
и, если он не просрочен, предоставляла вам доступ к указываемому объекту. Это делается путем создания указателя std::shared_ptr
из указателя std::weak_ptr
. Операция имеет две разновидности, в зависимости от того, что должно произойти в ситуации, когда std::weak_ptr
оказывается просроченным при попытке создания из него std::shared_ptr
. Одной разновидностью является std::weak_ptr::lock
, которая возвращает std::shared_ptr
. Этот указатель нулевой, если std::weak_ptr
просрочен:
std::shared_ptr spw1 // Если wpw просрочен,
= wpw. lock(); // spw1 - нулевой
auto spw2 = wpw. lock(); // То же самое, но с auto
Второй разновидностью является конструктор std::shared_ptr
, принимающий std::weak_ptr
в качестве аргумента. В этом случае, если std::weak_ptr
просрочен, генерируется исключение:
std::shared_ptrspw3(wpw); // Если wpw просрочен, гене-
// рируется std::bad_weak_ptr
Но вас, вероятно, интересует, зачем вообще нужен std::weak_ptr
. Рассмотрим фабричную функцию, которая производит интеллектуальные указатели на объекты только для чтения на основе уникальных значений идентификаторов. В соответствии с советом из раздела 4.1, касающегося возвращаемых типов фабричных функций, она возвращает std::unique_ptr
:
std::unique_ptr loadWidget(WidgetID id);
Если loadWidget
является дорогостоящим вызовом (например, из-за файловых операций ввода-вывода или обращения к базе данных), а идентификаторы часто используются повторно, разумной оптимизацией будет написание функции, которая делает то же, что и loadWidget
, но при этом кеширует результаты. Засорение кеша всеми затребованными Widget
может само по себе привести к проблемам производительности, так что другой разумной оптимизацией является удаление кешированных Widget
, когда они больше не используются.
Для такой кеширующей фабричной функции возвращаемый тип std::unique_ptr
не является удачным выбором. Вызывающий код, определенно, получает интеллектуальные указатели на кешированные объекты, и время жизни полученных объектов также определяется вызывающим кодом. Однако кеш также должен содержать указатели на эти же объекты. Указатели кеша должны иметь возможность обнаруживать свое висячее состояние, поскольку когда клиенты фабрики заканчивают работу с объектом, возвращенным ею, этот объект уничтожается, и соответствующая запись кеша становится висячей. Следовательно, кешированные указатели должны представлять собой указатели std::weak_ptr
, которые могут обнаруживать, что стали висячими. Это означает, что возвращаемым типом фабрики должен быть std::shared_ptr
, так как указатели std::weak_ptr могут обнаруживать, что стали висячими, только когда время жизни объектов управляется указателями std::shared_ptr
.
Вот как выглядит быстрая и неаккуратная реализация кеширующей версии loadWidget
:
std::shared_ptr fastLoadWidget(WidgetID id) {
static std::unordered_map
std::weak_ptr> cache;
auto objPtr // objPtr является std::shared_ptr
= cache[id].lock(); // для кешированного объекта и
// нулевым указателем для объекта,
// отсутствующего в кеше
if (!objPtr) { // При отсутствии в кеше
objPtr // объект загружается
= loadWidget(id);
cache[id] = objPtr; // и кешируется
}
return objPtr;
}
Эта реализация использует один из контейнеров С++11, представляющий собой хеш-таблицу ( std::unordered_map
), хотя здесь и не показаны хеширование WidgetID
и функции сравнения, которые также должны присутствовать в коде.
Интервал:
Закладка: