Скотт Мейерс - Эффективный и современный С++. 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 - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
• Инкремент и декремент счетчика ссылок должны быть атомарными, поскольку могут присутствовать одновременное чтение и запись в разных потоках. Например, std::shared_ptr
, указывающий на ресурс в одном потоке, может выполнять свой деструктор (тем самым уменьшая количество ссылок на указываемый им ресурс), в то время как в другом потоке указатель std::shared_ptr
на тот же объект может быть скопирован (а следовательно, увеличивает тот же счетчик ссылок). Атомарные операции обычно медленнее неатомарных, так что несмотря на то, что обычно счетчики ссылок имеют размер в одно слово, следует рассматривать их чтение и запись как относительно дорогостоящие операции.
Возбудил ли я ваше любопытство, когда написал, что конструкторы std::shared_ptr
“обычно” увеличивают счетчик ссылок для указываемого объекта? При создании std::shared_ptr
, указывающего на объект, всегда добавляется еще один интеллектуальный указатель std::shared_ptr
, так почему же счетчик ссылок может увеличиваться не всегда ?
Из-за перемещающего конструирования — вот почему. Перемещающее конструирование указателя std::shared_ptr
из другого std::shared_ptr
делает исходный указатель нулевым, а это означает, что старый std::shared_ptr
перестает указывать на ресурс в тот же момент, в который новый std::shared_ptr
начинает это делать. В результате изменение значения счетчика ссылок не требуется. Таким образом, перемещение std::shared_ptr
оказывается быстрее копирования: копирование требует увеличения счетчика ссылок, а перемещение — нет. Это справедливо как для присваивания, так и для конструирования, так что перемещающее конструирование быстрее копирующего конструирования, а перемещающее присваивание быстрее копирующего присваивания.
Подобно std::unique_ptr
(см. раздел 4.1), std::shared_ptr
в качестве механизма удаления ресурса по умолчанию использует delete
, но поддерживает и пользовательские удалители. Однако дизайн этой поддержки отличается от дизайна для std::unique_ptr
. Для std::unique_ptr
тип удалителя является частью типа интеллектуального указателя. Для std::shared_ptr
это не так:
auto loggingDel = [](Widget *pw) // Пользовательский удалитель
{ // (как в разделе 4.1)
makeLogEntry(pw);
delete pw;
};
std::unique_ptr< // Тип удалителя является
Widget, decltype(loggingDel) // частью типа указателя
> upw(new Widget, loggingDel);
std::shared_ptr // Тип удалителя не является
spw(new Widget, loggingDel); // частью типа указателя
Дизайн std::shared_ptr
более гибок. Рассмотрим два указателя std::shared_ptr,
каждый со своим пользовательским удалителем разных типов (например, из-за того, что пользовательские удалители определены с помощью лямбда-выражений):
auto customDeleter1 = [](Widget *pw) { … }; // Пользовательские
auto customDeleter2 = [](Widget *pw) { … }; // удалители
// разных типов
std::shared_ptr pw1(new Widget, customDeleter1);
std::shared_ptr pw2(new Widget, customDeleter2);
Поскольку pw1
и pw2
имеют один и тот же тип, они могут быть помещены в контейнер объектов этого типа:
std::vector> vpw( pw1, pw2 };
Они также могут быть присвоены один другому и переданы функции, принимающей параметр типа std::shared_ptr
. Ни одно из этих действий не может быть выполнено с указателями std::unique_ptr
, которые отличаются типами пользовательских удалителей, так как эти типы влияют на тип самого std::unique_ptr
.
Другим отличием от std::unique_ptr
является то, что указание пользовательского удалителя не влияет на размер объекта std::shared_ptr
. Независимо от удалителя объект std::shared_ptr
имеет размер, равный размеру двух указателей. Это хорошая новость, но она должна привести вас в замешательство. Пользовательские удалители могут быть функциональными объектами, а функциональные объекты могут содержать любое количество данных, а значит, быть любого размера. Как же std::shared_ptr
может обращаться к удалителю произвольного размера, не используя при этом дополнительной памяти?
А он и не может. Ему приходится использовать дополнительную память, но эта память не является частью объекта std::shared_ptr
. Она располагается в динамической памяти или, если создатель std::shared_ptr
воспользуется поддержкой со стороны std::shared_ptr
пользовательских распределителей памяти, там, где выделит память такой распределитель. Ранее я отмечал, что объект std::shared_ptr
содержит указатель на счетчик ссылок для объекта, на который он указывает. Это так, но это немного не так, поскольку счетчик ссылок является частью большей структуры данных, известной под названием управляющий блок (control block). Управляющий блок имеется для каждого объекта, управляемого указателями std::shared_ptr
. Управляющий блок в дополнение к счетчику ссылок содержит копию пользовательского удалителя, если таковой был указан. Если указан пользовательский распределитель памяти, управляющий блок содержит и его копию. Управляющий блок может также содержать дополнительные данные, включающие, как поясняется в разделе 4.4, вторичный счетчик ссылок, известный как слабый счетчик, но в данном разделе мы его игнорируем. Мы можем представить память, связанную с объектом std::shared_ptr
, как имеющую следующий вид:

Управляющий блок объекта настраивается функцией, создающей первый указатель std::shared_ptr
на объект. Как минимум это то, что должно быть сделано. В общем случае функция, создающая указатель std::shared_ptr
на некоторый объект, не может знать, не указывает ли на этот объект некоторый другой указатель std::shared_ptr
, так что при создании управляющего блока должны использоваться следующие правила.
• Функция std::make_shared
(см. раздел 4.4) всегда создает управляющий блок. Она производит новый объект, на который будет указывать интеллектуальный указатель, так что в момент вызова std::make_shared
управляющий блок для этого объекта, определенно, не существует.
• Управляющий блок создается тогда, когда указатель std::shared_ptr
создается из указателя с исключительным владением (т.e. std::unique_ptr
или std::auto_ptr
). Указатели с исключительным владением не используют управляющие блоки, так что никакого управляющего блока для указываемого объекта не существует. (Как часть своего построения std::shared_ptr
осуществляет владение указываемым объектом, так что указатель с исключительным владением становится нулевым.)
• Когда конструктор std::shared_ptr
вызывается с обычным указателем, он создает управляющий блок. Если вы хотите создать std::shared_ptr
из объекта, у которого уже имеется управляющий блок, вы предположительно передаете в качестве аргумента конструктора std::shared_ptr
или std::weak_ptr
(см. раздел 4.3), а не обычный указатель. Конструкторы std::shared_ptr
, принимающие в качестве аргументов указатели std::shared_ptr
или std::weak_ptr
, не создают новые управляющие блоки, поскольку могут воспользоваться управляющими блоками, на которые указывают переданные им интеллектуальные указатели.
Интервал:
Закладка: