Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14 - [57]
>auto pw = new Widget; // pw - обычный указатель
>…
>std::shared_ptr
> spw1(pw, loggingDel); // Создание управляющего блока для *pw
>…
>std::shared_ptr
> spw2(pw, loggingDel); // управляющего блока для *pw!
Создание обычного указателя >pw
, указывающего на динамически выделенный объект, — плохое решение, поскольку оно противоречит главному совету всей главы: предпочитайте интеллектуальные указатели обычным указателям. (Если вы забыли, откуда взялся этот совет, заново прочтите первую страницу данной главы.) Но пока что забудем об этом. Строка, в которой создается >pw
, представляет собой стилистическую мерзость, но она по крайней мере не приводит к неопределенному поведению.
Далее вызывается конструктор для >spw1
, которому передается обычный указатель, так что этот конструктор создает управляющий блок (и тем самым счетчик ссылок) для того, на что он указывает. В данном случае это >*pw
(т.e. объект, на который указывает >pw
). Само по себе это не является чем-то страшным, но далее с тем же обычным указателем вызывается конструктор для >spw2
, и он также создает управляющий блок (а следовательно, и счетчик ссылок) для >*pw
. Объект >*pw
, таким образом, имеет два счетчика ссылок, каждый из которых в конечном итоге примет нулевое значение, и это обязательно приведет к попытке уничтожить объект >*pw
дважды. Второе уничтожение и будет ответственно за неопределенное поведение.
Из этого можно вынести как минимум два урока, касающиеся применения >std::shared_ptr
. Во-первых, пытайтесь избегать передачи обычных указателей конструктору >std::shared_ptr
. Обычной альтернативой этому является применение функции >std::make_shared
(см. раздел 4.4), но в примере выше мы использовали пользовательские удалители, а это невозможно при использовании >std::make_shared
. Во-вторых, если вы вынуждены передавать обычный указатель конструктору >std::shared_ptr
, передавайте непосредственно результат оператора >new
, а не используйте переменную в качестве посредника. Если первую часть кода переписать следующим образом,
>std::shared_ptr
> spw1(new Widget, // Непосредственное использование new
> loggingDel);
то окажется гораздо труднее создать второй >std::shared_ptr
из того же обычного указателя. Вместо этого автор кода, создающего >spw2
, естественно, будет использовать в качестве аргумента инициализации >spw1
(т.e. будет вызывать копирующий конструктор >std::shared_ptr
), и это не будет вести ни к каким проблемам:
>std::shared_ptr
> spw2(spw1); // управляющий блок, что и spw1
Особенно удивительный способ, которым применение переменных с обычными указателями в качестве аргументов конструкторов >std::shared_ptr
может привести к множественным управляющим блокам, включает указатель >this
. Предположим, что наша программа использует указатели >std::shared_ptr
для управления объектами >Widget
и у нас есть структура данных, которая отслеживает объекты >Widget
, которые были обработаны:
>std::vector<std::shared_ptr
Далее, предположим, что >Widget
имеет функцию-член, выполняющую эту обработку:
>class Widget {
>public:
> …
> void process();
>};
Вот вполне разумно выглядящий подход к написанию >Widget::process
:
>void Widget::process() {
> … // Обработка Widget
> processedWidgets.emplace_back(this); // Добавление в список
>} // обработанных Widget;
> // это неправильно!
Комментарий о неправильности кода относится не к использованию >emplace_back
, а к передаче >this
в качестве аргумента. (Если вы не знакомы с >emplace_back
, обратитесь к разделу 8.2.) Этот код будет компилироваться, но он передает обычный указатель (>this
) контейнеру указателей >std::shared_ptr
. Конструируемый таким образом указатель >std::shared_ptr
будет создавать новый управляющий блок для указываемого >Widget
(т.e. для объекта >*this
). Это не выглядит ужасным до тех пор, пока вы не понимаете, что, если имеются указатели >std::shared_ptr
вне функции-члена, которые уже указывают на этот объект >Widget
, вам не избежать неопределенного поведения.
API >std::shared_ptr
включает средство для ситуаций такого рода. Вероятно, его имя — наиболее странное среди всех имен стандартной библиотеки: >std::enable_shared_from_this
. Это шаблон базового класса, который вы наследуете, если хотите, чтобы класс, управляемый указателями >std::shared_ptr
, был способен безопасно создавать >std::shared_ptr
из указателя >this
. В нашем примере >Widget
будет унаследован от >std::enable_shared_from_this
следующим образом:
>class Widget: public std::enable_shared_from
>public:
> …
> void process();
>};
Как я уже говорил, >std::enable_shared_from_this
является шаблоном базового класса. Его параметром типа всегда является имя производного класса, так что класс >Widget
порождается от >std::enable_shared_from_this
. Если идея производного класса, порожденного от базового класса, шаблонизированного производным, вызывает у вас головную боль, попытайтесь об этом не думать. Этот код совершенно законный, а соответствующий шаблон проектирования хорошо известен, имеет стандартное имя, хотя и почти такое же странное, как
В этой книге известный автор Скотт Мейерс раскрывает секреты настоящих мастеров, позволяющие добиться максимальной эффективности при работе с библиотекой STL.Во многих книгах описываются возможности STL, но только в этой рассказано о том, как работать с этой библиотекой. Каждый из 50 советов книги подкреплен анализом и убедительными примерами, поэтому читатель не только узнает, как решать ту или иную задачу, но и когда следует выбирать то или иное решение — и почему именно такое.
Когда приходится инкапсулировать, то иногда лучше меньше, чем большеЯ начну со следующего утверждения: Если вы пишете функцию, которая может быть выполнена или как метод класса, или быть внешней по отношению к классу, Вы должны предпочесть ее реализацию без использования метода. Такое решение увеличивает инкапсуляцию класса. Когда Вы думаете об использовании инкапсуляции, Вы должны думать том, чтобы не использовать методы.Удивлены? Читайте дальше.
Java Enterprise Edition (Java EE) остается одной из ведущих технологий и платформ на основе Java. Данная книга представляет собой логичное пошаговое руководство, в котором подробно описаны многие спецификации и эталонные реализации Java EE 7. Работа с ними продемонстрирована на практических примерах. В этом фундаментальном издании также используется новейшая версия инструмента GlassFish, предназначенного для развертывания и администрирования примеров кода. Книга написана ведущим специалистом по обработке запросов на спецификацию Java EE, членом наблюдательного совета организации Java Community Process (JCP)
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
Эта книга научит вас, как разрабатывать программное обеспечение для платформы J2ME компании «Sun Microsystems». Эта книга придерживается стиля учебного пособия, это не справочное руководство.Цель — дать вам твердую основу в понятиях и техниках, которая даст вам возможность решиться на самостоятельную разработку качественных приложений.
Это практическое руководство разработчика программного обеспечения на Visual Basic .NET и ADO.NET, предназначенное для создания приложений баз данных на основе WinForms, Web-форм и Web-служб. В книге описываются практические способы решения задач доступа к данным, с которыми сталкиваются разработчики на Visual Basic .NET в своей повседневной деятельности. Книга начинается с основных сведений о создании баз данных, использовании языка структурированных запросов SQL и системы управления базами данных Microsoft SQL Server 2000.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
Embedded system software. General requirements for development and documentationСтандарт подготовлен в развитие ГОСТ Р ИСО/МЭК 12207-99 «Информационная технология. Процессы жизненного цикла программных средств» с целью учета специфики разработки и документирования программного обеспечения встроенных систем реального времени.