Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14 - [58]

Шрифт
Интервал

. Это имя — Странно повторяющийся шаблон (The Curiously Recurring Template Pattern — CRTP). Если вы хотите узнать о нем побольше, расчехлите свой поисковик, поскольку сейчас мы возвращаемся к нашему барану по имени >std::enable_shared_from_this.

Шаблон >std::enable_shared_from_this определяет функцию-член, которая создает >std::shared_ptr для текущего объекта, но делает это, не дублируя управляющие блоки. Это функция-член >shared_from_this, и вы должны использовать ее в функциях-членах тогда, когда вам нужен >std::shared_ptr, который указывает на тот же объект, что и указатель >this. Вот как выглядит безопасная реализация >Widget::process:

>void Widget::process() {

> // Как и ранее, обработка Widget

> …

> // Добавляем std::shared_ptr, указывающий на

> // текущий объект, в processedWidgets

> processedWidgets.emplace_back(shared_from_this());

>}

Внутри себя >shared_from_this ищет управляющий блок текущего объекта и создает новый >std::shared_ptr, который использует этот управляющий блок. Дизайн функции полагается на тот факт, что текущий объект имеет связанный с ним управляющий блок. Чтобы это было так, должен иметься уже существующий указатель >std::shared_ptr (например, за пределами функции-члена, вызывающей >shared_from_this), который указывает на текущий объект. Если такого >std::shared_ptr нет (т.e. если текущий объект не имеет связанного с ним управляющего блока), результатом будет неопределенное поведение, хотя обычно >shared_from_this генерирует исключение.

Чтобы препятствовать клиентам вызывать функции-члены, в которых используется >shared_from_this, до того как на объект будет указывать указатель >std::shared_ptr, классы, наследуемые от >std::enable_shared_from_this, часто объявляют свои конструкторы как private и заставляют клиентов создавать объекты путем вызова фабричных функций, которые возвращают указатели >std::shared_ptr. Например, класс >Widget может выглядеть следующим образом:

>class Widget: public std::enable_shared_from_this {

>public:

> // Фабричная функция, пересылающая

> // аргументы закрытому конструктору:

template

> static std::shared_ptr create(Ts&&... params);

> …

> void process(); // Как и ранее

> …

>private:

> …               // Конструкторы

>};

В настоящее время вы можете только смутно припоминать, что наше обсуждение управляющих блоков было мотивировано желанием понять, с какими затратами связано применение >std::shared_ptr. Теперь, когда мы понимаем, как избегать создания слишком большого количества управляющих блоков, вернемся к нашей первоначальной теме.

Управляющий блок обычно имеет размер в несколько слов, хотя пользовательские удалители и распределители памяти могут его увеличить. Обычная реализация управляющего блока оказывается более интеллектуальной, чем можно было бы ожидать. Она применяет наследование, и при этом даже имеются виртуальные функции. (Все это требуется для того, чтобы обеспечить корректное уничтожение указываемого объекта.) Это означает, что применение указателей >std::shared_ptr берет на себя также стоимость механизма виртуальной функции, используемой управляющим блоком.

Возможно, после того как вы прочли о динамически выделяемых управляющих блоках, удалителях и распределителях неограниченного размера, механизме виртуальных функций и атомарности работы со счетчиками ссылок, ваш энтузиазм относительно >std::shared_ptr несколько угас. Это нормально.

Они не являются наилучшим решением для любой задачи управления ресурсами. Но для предоставляемой ими функциональности цена >std::shared_ptr весьма разумна. В типичных условиях, когда использованы удалитель и распределитель памяти по умолчанию, а >std::shared_ptr создается с помощью >std::make_shared, размер управляющего блока составляет около трех слов, и его выделение, по сути, ничего не стоит. (Оно встроено в выделение памяти для указываемого им объекта. Дополнительная информация об этом приведена в разделе 4.4.) Разыменование >std::shared_ptr не более дорогостояще, чем разыменование обычного указателя. Выполнение операций, требующих работы со счетчиком ссылок (например, копирующий конструктор или копирующее присваивание, удаление) влечет за собой одну или две атомарные операции, но эти операции обычно отображаются на отдельные машинные команды, так что, хотя они могут быть дороже неатомарных команд, они все равно остаются отдельными машинными командами. Механизм виртуальных функций в управляющем блоке обычно используется только однажды для каждого объекта, управляемого указателями >std::shared_ptr: когда происходит уничтожение объекта.

В обмен на эти весьма скромные расходы вы получаете автоматическое управление временем жизни динамически выделяемых ресурсов. В большинстве случаев применение >std::shared_ptr значительно предпочтительнее, чем ручное управление временем жизни объекта с совместным владением. Если вы сомневаетесь, можете ли вы позволить себе использовать >std::shared_ptr, подумайте, точно ли вам нужно обеспечить совместное владение. Если вам достаточно или даже может быть достаточно исключительного владения, лучшим выбором является


Еще от автора Скотт Мейерс
Эффективное использование STL

В этой книге известный автор Скотт Мейерс раскрывает секреты настоящих мастеров, позволяющие добиться максимальной эффективности при работе с библиотекой STL.Во многих книгах описываются возможности STL, но только в этой рассказано о том, как работать с этой библиотекой. Каждый из 50 советов книги подкреплен анализом и убедительными примерами, поэтому читатель не только узнает, как решать ту или иную задачу, но и когда следует выбирать то или иное решение — и почему именно такое.


Как функции, не являющиеся методами, улучшают инкапсуляцию

Когда приходится инкапсулировать, то иногда лучше меньше, чем большеЯ начну со следующего утверждения: Если вы пишете функцию, которая может быть выполнена или как метод класса, или быть внешней по отношению к классу, Вы должны предпочесть ее реализацию без использования метода. Такое решение увеличивает инкапсуляцию класса. Когда Вы думаете об использовании инкапсуляции, Вы должны думать том, чтобы не использовать методы.Удивлены? Читайте дальше.


Рекомендуем почитать
Изучаем Java EE 7

Java Enterprise Edition (Java EE) остается одной из ведущих технологий и платформ на основе Java. Данная книга представляет собой логичное пошаговое руководство, в котором подробно описаны многие спецификации и эталонные реализации Java EE 7. Работа с ними продемонстрирована на практических примерах. В этом фундаментальном издании также используется новейшая версия инструмента GlassFish, предназначенного для развертывания и администрирования примеров кода. Книга написана ведущим специалистом по обработке запросов на спецификацию Java EE, членом наблюдательного совета организации Java Community Process (JCP)


DirectX 8. Начинаем работу с DirectX Graphics

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Платформа J2Me

Эта книга научит вас, как разрабатывать программное обеспечение для платформы J2ME компании «Sun Microsystems». Эта книга придерживается стиля учебного пособия, это не справочное руководство.Цель — дать вам твердую основу в понятиях и техниках, которая даст вам возможность решиться на самостоятельную разработку качественных приложений.


Обработка баз данных на Visual Basic.NET

Это практическое руководство разработчика программного обеспечения на 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 «Информационная технология. Процессы жизненного цикла программных средств» с целью учета специфики разработки и документирования программного обеспечения встроенных систем реального времени.