Скотт Мейерс - Эффективный и современный С++. 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 - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
decltype(delInvmt)>
makeInvestment(Ts&&... params) {
std::unique_ptr
decltype(delInvmt)> // Возвращаемый
pInv(nullptr, delInvmt); // указатель
if ( /* Должен быть создан объект Stock */ ) {
pInv.reset(new Stock(std::forward(params)...));
}
else if ( /* Должен быть создан объект Bond */ ) {
pInv.reset(new Bond(std::forward(params)...));
}
else if ( /* Должен быть создан объект RealEstate */ ) {
pInv.reset(new RealEstate(std::forward(params)...));
}
return pInv;
}
Сейчас я поясню вам, как это работает, но сначала рассмотрим, как все это выглядит с точки зрения вызывающей функции. Предположим, что вы сохраняете результат вызова makeInvestment
в переменной, объявленной как auto
, и тем самым остаетесь в блаженном неведении о том, что используемый вами ресурс требует специальной обработки в процессе удаления. Вы просто купаетесь в этом блаженстве, поскольку использование std::unique_ptr
означает, что вам не надо рассматривать самостоятельно вопросы освобождения ресурса, тем более не требуется беспокоиться о том, чтобы это уничтожение выполнялось в точности один раз на каждом пути в программе. Обо всем этом std::unique_ptr
заботится автоматически. С точки зрения клиента интерфейс makeInvestment
— просто конфетка.
Реализация очень красивая, если вы понимаете следующее.
• delInvmt
представляет собой пользовательский удалитель для объекта, возвращаемого функцией makeInvestment
. Все функции пользовательских удалителей принимают обычный указатель на удаляемый объект и затем выполняют все необходимые действия по его удалению. В нашем случае действие заключается в вызове makeLogEntry
и последующем применении delete
. Применение лямбда-выражения для создания delInvmt
удобно, но, как вы вскоре увидите, оно также гораздо эффективнее написания обычной функции.
• Когда используется пользовательский удалитель, его тип должен быть указан в качестве второго аргумента типа std::unique_ptr
. В нашем случае это тип delInvmt
, и именно поэтому возвращаемым типом makeInvestment
является std::unique_ptr
. (О том, что такое decltype
, рассказывается в разделе 1.3.)
• Основная стратегия makeInvestment
состоит в создании нулевого указателя std::unique_ptr
, после чего он делается указывающим на объект соответствующего типа и возвращается из функции. Для связи пользовательского удалителя delInvmt
с pInv
мы передаем его в качестве второго аргумента конструктора.
• Попытка присвоить обычный указатель (например, возвращенный оператором new
) указателю std::unique_ptr
компилироваться не будет, поскольку она будет содержать неявное преобразование обычного указателя в интеллектуальный. Такие неявные преобразования могут быть проблематичными, так что интеллектуальные указатели С++11 их запрещают. Вот почему для того, чтобы pInv
взял на себя владение объектом, созданным с помощью оператора new
, применяется вызов reset
.
• С каждым использованием new
мы применяем std::forward
для прямой передачи аргументов, переданных в makeInvestment
(см. раздел 5.3). Это делает всю информацию, предоставляемую вызывающей функцией, доступной конструкторам создаваемых объектов.
• Пользовательский удалитель получает параметр типа Investment*
. Независимо от фактического типа объекта, создаваемого в функции makeInvestment
(т.e. Stock
, Bond
или RealEstate
), он в конечном итоге будет удален с помощью оператора delete
в лямбда-выражении как объект Investment*
. Это означает, что мы удаляем производный класс через указатель на базовый класс. Чтобы это сработало, базовый класс Investment
должен иметь виртуальный деструктор:
class Investment {
public:
…
virtual ~Investment();// Важная часть дизайна
};
В С++14 существование вывода возвращаемого типа функции (раздел 1.3) означает, что makeInvestment
можно реализовать проще и несколько более инкапсулированно:
template
automakeInvestment(Ts&&... params) // С++14
{
auto delInvmt = [](Investment* pInvestment)
{ // Теперь размещается в
makeLogEntry(pInvestment); // пределах makeInvestment
delete pInvestment;
};
std::unique_ptr
pInv(nullptr, delInvmt); // Как и ранее
if ( … ) // Как и ранее
pInv.reset(new Stock(std::forward(params)...));
else if ( … ) // Как и ранее
pInv.reset(new Bond(std::forward(params)...));
else if ( … ) // Как и ранее
pInv.reset(new RealEstate(std::forward(params)...));
return pInv; // Как и ранее
}
Ранее я отмечал, что при использовании удалителя по умолчанию (т.e. delete
) можно разумно предположить, что объекты std::unique_ptr
имеют тот же размер, что и обычные указатели. Когда в игру вступают пользовательские указатели, это предположение перестает быть верным. Удалители являются указателями на функции, которые в общем случае приводят к увеличению размера std::unique_ptr
на слово или два. Для удалителей, являющихся функциональными объектами, изменение размера зависит от того, какое состояние хранится в функциональном объекте. Функциональные объекты без состояний (например, получающиеся из лямбда-выражений без захватов) не приводят к увеличению размеров, а это означает что когда пользовательский удалитель может быть реализован как функция или как лямбда-выражение, то реализация в виде лямбда- выражения предпочтительнее:
auto delInvmt1 = [](Investment* pInvestment)
{ // Пользовательский удалитель
makeLogEntry(pInvestment); // как лямбда-выражение
delete pInvestment; // без состояния
};
template // Возвращаемый тип имеет размер
std::unique_ptr<Investment, // Investment*
decltype(delInvmt1)>
makeInvestment(Ts&&... args);
void delInvmt2(Investment* pInvestment) // Пользовательский
{ // удалитель как
makeLogEntry(pInvestment); // функция
delete pInvestment;
}
template // Возвращаемый тип имеет размер
std::unique_ptr<Investment, // Investment* плюс как минимум
void (*)(Investment*)> // размер указателя на функцию!
makeInvestment(Ts&&... params);
Удалители в виде функциональных объектов с большим размером состояния могут привести к значительным размерам объектов std::unique_ptr
. Если вы обнаружите, что пользовательский удалитель делает ваш интеллектуальный указатель std::unique_ptr
неприемлемо большим, вам, вероятно, стоит изменить свой дизайн.
Фабричные функции — не единственный распространенный способ использования std::unique_ptr
. Эти интеллектуальные указатели еще более популярны в качестве механизма реализации идиомы Pimpl (указателя на реализацию). Соответствующий код не сложен, но его рассмотрение отложено до раздела 4.5, посвященного данной теме.
Интервал:
Закладка: