Скотт Мейерс - Эффективный и современный С++. 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 - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
В С++11 два ограничения предотвращают объявление функций-членов Point
setX
и setY
как constexpr
. Во-первых, они модифицируют объект, с которым работают, а в С++11 функции-члены constexpr
неявно являются const
. Во-вторых, они имеют возвращаемый тип void
, а void
не является литеральным типом в С++11. Оба эти ограничения сняты в С++14, так что в С++14 даже функции установки полей Point
могут быть объявлены как constexpr
:
class Point {
public:
constexprvoid setX(double newX) noexcept // С++14
{ x = newX;}
constexpr void setY(double newY) noexcept // С++14
{ y = newY; }
};
Это делает возможным написание функций наподобие следующей:
// Возвращает отражение точки p
// oтнocитeльнo начала координат (С++14)
constexpr Point reflection(const Point& p) noexcept {
Point result; // Нeкoнcтантный объект Point
result.setX(-p.xValue()); // Установка его полей x и y
result.setY(-p.yValue());
return result; // Возврат копии
}
Соответствующий клиентский код имеет вид:
constexpr Point p1(9.4, 27.7); // Как и выше
constexpr Point p2(28.8, 5.3);
constexpr auto mid = midpoint(p1, p2);
constexpr auto reflectedMid = // reflectedMid пpeдcтавляeт
reflection(mid); // собой (-19.1 -16.5) и
// известно во время компиляции
Совет из этого раздела заключается в том, чтобы использовать constexpr
везде, где это только возможно, и теперь, надеюсь, вам понятно, почему: и объекты constexpr
, и constexpr
-функции могут применяться в более широком диапазоне контекстов, чем объекты и функции, не являющиеся constexpr
. Применяя constexpr
, где это возможно, вы максимизируете диапазон ситуаций, в которых ваши объекты и функции могут быть использованы.
Важно отметить, что constexpr
является частью интерфейса объекта или функции. constexpr
провозглашает: “Меня можно использовать в контексте, где для С++ требуется константное выражение”. Если вы объявляете объект или функцию как constexpr
, клиенты могут использовать их в указанных контекстах. Если позже вы решите, что такое использование constexpr
было ошибкой, и удалите его, то это может привести к тому, что большое количество клиентского кода перестанет компилироваться. (Простое действие, заключающееся в добавлении в функцию отладочного вывода для отладки или настройки производительности может привести к таким проблемам, поскольку инструкции ввода-вывода в общем случае в constexpr
-функциях недопустимы.) Часть “где это возможно” совета является вашей доброй волей на придание долгосрочного характера данному ограничению на объекты и функции, к которым вы его применяете.
• Объекты constexpr
являются константными и инициализируются объектами, значения которых известны во время компиляции.
• Функции constexpr
могут производить результаты времени компиляции при вызове с аргументами, значения которых известны во время компиляции.
• Объекты и функции constexpr
могут использоваться в более широком диапазоне контекстов по сравнению с объектами и функциями, не являющимися constexpr
.
• constexpr
является частью интерфейса объектов и функций.
3.10. Делайте константные функции-члены безопасными в смысле потоков
Если мы работаем в области математики, нам может пригодиться класс, представляющий полиномы. В этом классе было бы неплохо иметь функцию для вычисления корней полинома, т.e. значений, при которых значение полинома равно нулю. Такая функция не должна модифицировать полином, так что ее естественно объявить как const
:
class Polynomial {
public:
using RootsType = // Структура данных, хранящая
std::vector; // значения, где полином равен нулю
… // (см. "using" в разделе 3.3)
RootsType roots() const;
};
Вычисление корней — трудная дорогостоящая операция, так что мы не хотим их вычислять до того, как они реально потребуются. Но если они нам требуются, то, определенно, требуются не один раз. Поэтому мы будем кешировать корни полиномов, если нам приходится их вычислять, и реализуем roots
так, чтобы функция возвращала кешированное значение. Вот как выглядит такой подход:
class Polynomial {
public:
using RootsType = std::vector;
RootsType roots() const {
if (!rootsAreValid) { // Если кеш некорректен,
… // вычисляем корни и сохраняем
// их в in rootVals
rootsAreValid = true;
}
return rootVals;
}
private:
mutable bool rootsAreValid{ false }; // См. инициализаторы
mutable RootsType rootVals{}; // в разделе 3.1
};
Концептуально roots
не изменяет объект Polynomial
, с которым работает, но в качестве части кеширующих действий может потребоваться изменение rootVals
и rootsAreValid
. Это классический случай использования mutable
, и именно поэтому эти данные-члены объявлены с данным модификатором.
Представим теперь, что два потока одновременно вызывают roots
для объекта Polynomial
:
Polynomial p;
…
/*----- Поток 1 ----- */ /*------- Поток 2 ------- */
auto rootsOfP = p.roots(); auto valsGivingZero = p.roots();
Этот клиентский код совершенно разумен. Функция roots
является константной функцией-членом, и это означает, что она представляет операцию чтения. Выполнение операций чтения несколькими потоками одновременно без синхронизации вполне безопасно. Как минимум предполагается, что это так. В данном случае это не так, поскольку в функции roots
один или оба эти потока могут попытаться изменить члены-данные rootsAreValid
и rootVals
. Это означает, что данный код может одновременно читать и записывать одни и те же ячейки памяти без синхронизации, а это — определение гонки данных. Такой код имеет неопределенное поведение.
Проблема заключается в том, что функция roots
объявлена как const
, но не является безопасной с точки зрения потоков. Объявление const
является корректным как в С++11, так и в С++98 (вычисление корней полинома не изменяет сам полином), так что коррекция нужна для повышения безопасности потоков.
Простейший способ решения проблемы обычно один: применение mutex
:
class Polynomial {
public:
using RootsType = std::vector;
RootsType roots() const {
std::lock_guard g(m);// Блокировка мьютекса
if (!rootsAreValid) { // Если кеш некорректен
… // Вычисление корней
rootsAreValid = true;
}
return rootVals;
} // Разблокирование
private:
mutable std::mutex m;
mutable bool rootsAreValid{ false };
mutable RootsType rootVals{};
};
Мьютекс std::mutex m
объявлен как mutable
, поскольку его блокировка и разблокирование являются неконстантными функциями, а в противном случае в константной функции-члене roots
мьютекс m
рассматривается как константный объект.
Интервал:
Закладка: