Скотт Мейерс - Эффективный и современный С++. 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 - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Можно получить значение x
в переменную y
, но это требует использования функций- членов load
и store
типа std::atomic
. Функция-член load
атомарно считывает значение std::atomic
, а функция-член store
атомарно его записывает. Для инициализации y
значением x
, после которой выполняется размещение значения x
в y
, код должен иметь следующий вид:
std::atomic y( x.load()); // Чтение x
y.store( x.load()); // Чтение x еще раз
Этот код компилируется, но тот факт, что чтение x
(с помощью x.load()
) является отдельным от инициализации или сохранения значения у
вызовом функции, делает очевидным то, что нет никаких оснований ожидать, что целая инструкция будет выполняться как единая атомарная операция.
Компиляторы могут “оптимизировать” приведенный код, сохраняя значение x
в регистре вместо двойного его чтения:
register = x.load(); // Чтение x в регистр
std::atomic y( register ); // Инициализация y
// значением регистра
y.store( register ); // Сохранение значения
// регистра в y
В результате, как можно видеть, чтение из x
выполняется только один раз, и этой разновидности оптимизации следует избегать при работе с особой памятью. (Эта оптимизация не разрешена при работе с переменными volatile
.) Таким образом, ситуация должна быть очевидна.
• std::atomic
применяется в параллельном программировании, но не для доступа к особой памяти.
• volatile
применяется для доступа к особой памяти, но не в параллельном программировании.
Поскольку std::atomic
и volatile
служат разным целям, они могут использоваться совместно:
volatile std::atomicvai; // Операции над vai атомарны
// и не могут быть удалены
// при оптимизации
Этот может оказаться полезным, когда vai соответствует ячейке отображаемого на память ввода-вывода, обращение к которой выполняется несколькими потоками.
Последнее примечание: некоторые разработчики предпочитают использовать функции-члены load
и store
типа std::atomic
даже там, где это не требуется, поскольку это четко указывает в исходном тексте на то, что данные переменные не являются “обычными”. Подчеркивание этого факта не является необоснованным. Доступ к std::atomic
обычно гораздо медленнее, чем к переменным, не являющимся std::atomic
, и мы уже видели, что использование std::atomic
предотвращает определенное переупорядочение кода, которое иначе было бы разрешено. Вызовы загрузок и сохранений std::atomic
могут тем самым помочь в идентификации потенциальных узких мест масштабируемости. С точки зрения корректности отсутствие вызова store
у переменной, предназначенной для передачи информации в другие потоки (например, флаг, указывающий доступность данных), может означать, что эта переменная не объявлена как std::atomic
, хотя должна быть таковой.
Однако в большей степени это вопрос стиля, и как таковой он не имеет отношения к выбору между std::atomic
и volatile
.
• std::atomic
применяется для обращения нескольких потоков к данным без использования мьютексов. Это инструмент параллельного программирования.
• volatile
применяется для памяти, чтения и записи которой не должны удаляться при оптимизации. Это инструмент для работы с особой памятью.
Глава 8
Тонкости
Для каждого общего метода или возможности С++ имеются условия, когда его применение является разумным, и обстоятельства, в которых его не следует применять. Обычно описание того, когда имеет смысл применение некоторого общего метода или возможности, достаточно простое, но в данной главе описываются два исключения из этого правила: общий метод (передача по значению) и общая возможность (размещение (emplacement)). Решение об их применении зависит от такого большого количества факторов, что лучший совет, какой я могу дать, сводится к рассмотрению возможности их применения. Тем не менее оба они являются важными составляющими эффективного современного программирования на С++, и приведенная в разделах этой главы информация необходима для принятия вами решения об их применении в своих программах.
8.1. Рассмотрите передачу по значению для копируемых параметров, которые легко перемещаются и всегда копируются
Некоторые параметры функций предназначаются для копирования [29] В данном разделе “копирование” параметра в общем случае означает его использование как источника для операций копирования или перемещения. Помните, что в С++ нет терминологического различия между копированием, выполняемым с помощью операции копирования, и копированием с помощью операции перемещения.
. Например, функция-член addName
может копировать свой параметр в закрытый контейнер. Для эффективности такая функция должна копировать аргументы, являющиеся lvalue, но перемещать аргументы, являющиеся rvalue:
class Widget {
public:
void addName( const std::string&newName) // lvalue;
{ names.push_back(newName); } // копируем
void addName( std::string&&newName) // rvalue;
{ names.push_back(std::move(newName)); } // перемещаем
// См. применение std::move в разделе 5.3
…
private:
std::vector names;
};
Этот способ работает, но требует двух функций, выполняющих, по сути, одни и те же действия. Это несколько раздражает: надо объявить две функции, реализовать две функции, документировать две функции и наконец поддерживать также две функции! Тьфу…
Кроме того, две функции будут и в объектном коде, что при определенных обстоятельствах тоже может напрягать. В данном конкретном случае обе функции, вероятно, будут встраиваемыми, так что этот вопрос не встанет, но если функции не встраиваемые, то в объектном коде будут они обе.
Альтернативный подход заключается в том, чтобы сделать addName
шаблоном функции, получающей универсальную ссылку (см. раздел 5.2):
class Widget {
public:
template // Получаем как lvalue,
void addName( T&& newName) // так и rvalue; lvalue
{ // копируем, rvalue перемещаем
names.push_back(std::forward(newName));
} // Применение std::forward описано в разделе 5.3
};
Это приводит к уменьшению количества исходного текста, с которым приходится работать, но применение универсальных ссылок влечет за собой другие сложности. Будучи шаблоном, реализация addName
обычно должна располагаться в заголовочном файле. В объектном коде такой подход может дать несколько функций, так как инстанцирование будет выполняться по-разному не только для lvalue и rvalue, но и для std::string
и типов, преобразуемых в std::string
(см. раздел 5.3). В то же время имеются типы аргументов, которые не могут быть переданы с помощью универсальной ссылки (см. раздел 5.8), и если клиент передаст аргументы некорректного типа, сообщения компилятора об ошибках могут быть приводящими в трепет (см. раздел 5.5).
Интервал:
Закладка: