Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14 - [97]
>void Widget::addFilter() const {
> filters.emplace_back(
> [=](int value) { return value % divisor == 0; }
> );
>}
>this->divisor
захватывается указатель >this
объекта >Widget
, а не >divisor
. Компиляторы рассматривают этот код так, как будто он написан следующим образом:
>void Widget::addFilter() const {
> auto currentObjectPtr = this;
> filters.emplace_back(
> [currentObjectPtr](int value)
> { return value % curentObjectPtr->divisor == 0;}
> );
>}
Понимание этого равносильно пониманию того, что жизнеспособность замыканий, вытекающих из этого лямбда-выражения, связана со временем жизни объекта >Widget
, копии указателя >this
которого в них содержатся. В частности, рассмотрим код, который в соответствии с главой 4 использует только интеллектуальные указатели:
>using FilterContainer = // Как и ранее
>std::vector
>FilterContainer filters; // Как и ранее
>void doSomeWork() {
> auto pw = // Создание Widget;
> std::make_unique
> // разделе 4.4
> pw->addFilter(); // Добавление фильтра
> // с Widget::divisor
>} // Уничтожение Widget; filters хранит висячий указатель!
Когда выполняется вызов >doSomeWork
, создается фильтр, зависящий от объекта >Widget
, созданного >std::make_unique
, т.e. фильтр, который содержит копию указателя на этот >Widget
, — указатель >this
объекта >Widget
. Этот фильтр добавляется в >filters
, но по завершении работы >doSomeWork
объект >Widget
уничтожается, так как >std::unique_ptr
управляет его временем жизни (см. раздел 4.1). С этого момента >filters
содержит элемент с висячим указателем.
Эта конкретная проблема может быть решена путем создания локальной копии члена-данных, который вы хотите захватить, и захвата этой копии:
>void Widget::addFilter() const {
> auto divisorCopy = divisor; // Копирование
> filters.emplace_back( // члена-данных
> [divisorCopy](int value) // Захват копии
> { return value % divisorCopy == 0; } // Ее использование
> );
>);
Чтобы быть честным, скажу, что при таком подходе захват по умолчанию по значению также будет работать:
>void Widget::addFilter() const {
> auto divisorCopy = divisor; // Копирование
> filters.emplace_back( // члена-данных
> [=](int value) // Захват копии
> { return value % divisorCopy == 0; } // Ее использование
> );
>}
но зачем искушать судьбу? Режим захвата по умолчанию делает возможным случайный захват >this
, когда вы думаете, в первую очередь, о захвате >divisor
.
В С++14 имеется лучший способ захвата члена-данных, заключающийся в использовании обобщенного захвата лямбда-выражения (см. раздел 6.2):
>void Widget::addFilter() const {
> filters.emplace_back( // С++14:
> [divisor = divisor](int value) // Копирование divisor
> // в замыкание
> { return value % divisor == 0; } // Использование копии
> );
>}
Однако такого понятия, как режим захвата по умолчанию для обобщенного захвата лямбда-выражения, не существует, так что даже в С++14 остается актуальным совет данного раздела — избегать режимов захвата по умолчанию.
Дополнительным недостатком режимов захвата по умолчанию является то, что они могут предполагать самодостаточность соответствующих замыканий и их изолированность от изменений внешних данных. В общем случае это не так, поскольку лямбда-выражения могут зависеть не только от локальных переменных и параметров (которые могут быть захвачены), но и от объектов со статическим временем хранения. Такие объекты определены в глобальной области видимости или области видимости пространства имен или объявлены как >static
внутри классов, функций или файлов. Эти объекты могут использоваться внутри лямбда-выражений, но не могут быть захвачены. Тем не менее спецификация режима захвата по умолчанию может создать именно такое впечатление. Рассмотрим преобразованную версию функции >addDivisorFilter
, с которой мы встречались ранее:
>void addDivisorFilter() {
> static auto calc1
> = computeSomeValue1() ; // Статический
> static auto calc2
> = computeSomeValue2(); // Статический
> static auto divisor = // Статический
> computeDivisor(calc1, calc2);
> filters.emplace_back(
> [=](int value) // Ничего не захватывает
> { return value % divisor == 0; } // Ссылка на статическую
> ); // переменную
> ++divisor; // Изменение divisor
>}
Случайный читатель этого кода может быть прощен за то, что, видя >[=]
, может подумать “Отлично, лямбда-выражение делает копию всех объектов, которые использует, и поэтому оно является самодостаточным”. Но это не так. Это лямбда-выражение не использует никакие нестатические локальные переменные, поэтому ничего не захватывается. Вместо этого код лямбда-выражения обращается к статической переменной >divisor
. Когда в конце каждого вызова >addDivisorFilter
выполняется увеличение >divisor
, все лямбда-выражения, которые были добавлены в >filters
с помощью данной функции, будут демонстрировать новое поведение (соответствующее новому значению
В этой книге известный автор Скотт Мейерс раскрывает секреты настоящих мастеров, позволяющие добиться максимальной эффективности при работе с библиотекой STL.Во многих книгах описываются возможности STL, но только в этой рассказано о том, как работать с этой библиотекой. Каждый из 50 советов книги подкреплен анализом и убедительными примерами, поэтому читатель не только узнает, как решать ту или иную задачу, но и когда следует выбирать то или иное решение — и почему именно такое.
Когда приходится инкапсулировать, то иногда лучше меньше, чем большеЯ начну со следующего утверждения: Если вы пишете функцию, которая может быть выполнена или как метод класса, или быть внешней по отношению к классу, Вы должны предпочесть ее реализацию без использования метода. Такое решение увеличивает инкапсуляцию класса. Когда Вы думаете об использовании инкапсуляции, Вы должны думать том, чтобы не использовать методы.Удивлены? Читайте дальше.
Разработчику часто требуется много сторонних инструментов, чтобы создавать и поддерживать проект. Система Git — один из таких инструментов и используется для контроля промежуточных версий вашего приложения, позволяя вам исправлять ошибки, откатывать к старой версии, разрабатывать проект в команде и сливать его потом. В книге вы узнаете об основах работы с Git: установка, ключевые команды, gitHub и многое другое.В книге рассматриваются следующие темы:основы Git;ветвление в Git;Git на сервере;распределённый Git;GitHub;инструменты Git;настройка Git;Git и другие системы контроля версий.
Рассмотрено все необходимое для разработки, компиляции, отладки и запуска приложений Java. Изложены практические приемы использования как традиционных, так и новейших конструкций объектно-ориентированного языка Java, графической библиотеки классов Swing, расширенной библиотеки Java 2D, работа со звуком, печать, способы русификации программ. Приведено полное описание нововведений Java SE 7: двоичная запись чисел, строковые варианты разветвлений, "ромбовидный оператор", NIO2, новые средства многопоточности и др.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
Python - объектно-ориентированный язык сверхвысокого уровня. Python, в отличии от Java, не требует исключительно объектной ориентированности, но классы в Python так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.