С. Мейерс - Эффективный и современный C++ (1114942), страница 37
Текст из файла (страница 37)
Для указателя std : : s h a r ed_pt r разница может оказатьсясущественной, так как копирование std : : shared_pt r требует атомарного инкрементасчетчика ссылок, в то время как перемещение std : : shared_ptr не требует никаких действий со счетчиком ссылок вообще. Чтобы безопасный с точки зрения исключений коддостиг уровня производительности небезопасного кода, нам надо применить std : : moveк spw для того, чтобы превратить его в rvalue (см. раздел 5. 1 ):/ / Эффективно и безопасноprocessWidget (std: :move (spw) ,computePriority ( ) ) ; / / в смысле исключенийЭто интересный метод, и его стоит знать, но обычно это не имеет особого значения, поскольку вы будете редко сталкиваться с причинами не использовать mаkе-функцию.
Еслиу вас нет убедительных причин поступать иначе, используйте mаkе-функции.1 54Гпава 4. И н теппектуапьные указателиСледует запомнить•По сравнению с непосредственным использованием new, mаkе-функции устраняютдублирование кода, повышают безопасность кода по отношению к исключениями в случае функций std : : ma ke_shared и std : : al l ocate_shared генерируют меньший по размеру и более быстрый код.•Ситуации, когда применение mаkе-функций неприемлемо, включают необходимость указания пользовательских удалителей и необходимость передачи инициализаторов в фигурных скобках.•Для указателей std : : shared_pt r дополнительными ситуациями, в которых применение mаkе-функций может быть неблагоразумным, являются классы с пользовательским управлением памятью и системы, в которых проблемы с объемом памятинакладываются на использование очень больших объектов и наличие указателейstd : : weak_ptr, время жизни которых существенно превышает время жизни указателей std : : shared_pt r.4 .
5 . П ри испол ьзовании идиомы указателяна реал изацию определяйте спе циал ьныефункции-чл ены в файл е реал изацииЕсли вам приходилось бороться со слишком большим временем построения приложения, вы, вероятно, знакомы с идиомой Pimpl (poiпter to implementation, указательна реализацию). Это методика, при которой вы заменяете члены-данные класса указателем на класс (или структуру) реализации, помещаете в него члены-данные, использовавшиеся в основном классе, и обращаетесь к ним опосредованно через указатель.
Предположим, например, что наш Widget имеет следующий вид:class Widget {puЫic :Widget ( ) ;// В заголовочном файле "widget . h"private :std : : st ring name ;std : : vector<douЫe> da t a ;Gadget gl , g2, gЗ ; / / Gadget-н е кий пользовательский тип};Поскольку члены-данные Widget имеют типы std : : s t r i ng, std : : vector и Gadget,для компиляции Widget должны присутствовать соответствующие заголовочные файлы,а это означает, что клиенты Widget должны включать с помощью директивы # includeзаголовочные файлы s t ring, vect or и gadget .
h. Эти заголовочные файлы увеличивают время компиляции клиентов W idget , а также делают этих клиентов зависящими от содержимого указанных заголовочных файлов. Если содержимое заголовочного4.S. При испоnьзовании идиомы указате nя на реаnизацию опредеnяйте специаnьные....1 55файла изменяется, клиенты Widget должны быть перекомпилированы. Стандартные заголовочные файлы string и vector меняются не слишком часто, но заголовочный файлgadget . h вполне может оказаться подвержен частым изменениям.Применение идиомы Pimpl в С++98 могло выполняться с помощью замены членовданных Widget обычным указателем на объявленную, но не определенную структуру:class Widget {puЫ i c :Widget ( ) ;-Widget ( ) ;1 1 Все еще в заголовочном файле "widget .
h"1 1 Деструктор необходим ( см . ниже )private :struct Impl ; / / Объявление структуры реализацииImpl *pimpl ; / / и указателя на нее};Поскольку W i dget больше не упоминает типы std : : st r i ng, std : : vector и Gadget,клиенты Widget больше не обязаны включать соответствующие заголовочные файлыдля этих типов. Это ускоряет компиляцию, а кроме того, означает, что если что-то в заголовочных файлах будет изменено, это не затронет клиенты W idget.Тип, который был объявлен, но не определен, называется неполным типом.Widget : : Impl является таким неполным типом.
С неполным типом можно сделать оченьнемногое, но в это немногое входит объявление указателя на него. Идиома Pimpl использует эту возможность.Первая часть идиомы Pimpl - объявление члена-данных, который представляет собой указатель на неполный тип. Вторая часть заключается в динамическом созданиии уничтожении объекта, хранящего члены-данные, использующиеся в исходном классе.Соответствующий код находится в файле реализации, например для Widget - в файлеwidget .
срр:# include# i nclude# include#include"widget . h ""gadget . h "<string><vector>1 1 Файл реализации "widget . cpp"s t ruct Widget : : Impl11 Определение Widget : : Implstd : : string name ;1 1 с членами -данными, ранееstd: : vector<douЫe> dat a ; / / находившимися в WidgetGadget gl , g2 , gЗ ;};Widget : : Widget ( ): p!mpl (new Impl )[}1 56/ / Создание членов-данных/ / для данного объекта WidgetГлава 4. И нтеллектуальн ые указателиWidget : : -Widget ( ){ dslete pimpl ; }11 Уничтожение членов -данных11 для данного объектаЗдесь я привожу директивы # include, чтобы было ясно, что общие зависимости от заголовочных файлов для std : : s t r ing, std : : vect o r и Gadget никуда не исчезли и продолжают существовать.
Однако эти зависимости перемещены из файла widget . h (видимого и используемого всеми клиентами класса Widget) в файл widget . срр (видимыйи используемый только реализацией Widget). Я также подчеркнул код динамическоговыделения и освобождения объекта Imp l . Необходимость освобождения этого объектапри уничтожении Widget приводит к необходимости деструктора Widget.Но я показал код С++98, от которого пахнет пылью вековой . . . Нет, пылью прошлого тысячелетия.
Он использует обычные указатели, обычные операторы new и delete,и вообще весь он слишком сырой'. Вся текущая глава построена на идее о том, что интеллектуальные указатели куда предпочтительнее обычных указателей, и если мы хотим динамически создавать объект W i dge t : : Imp l в конструкторе W i dget и должныуничтожать его вместе с Widget, то для нас отлично подойдет и нтеллектуальный указатель std : : un i que_ptr (см. раздел 4.
1 ). Заменяя обычный указатель pimpl указателемstd : : unique_pt r, мы получим следующий код для заголовочного файлаclass Widget {puЫic :Widget ( ) ;11 В файле "widget . h "private :struct Impl ;std: : unique_;> tr<Impl> pimpl ;};/ / Интеллектуальный указатель/ / вместо обычногои для файла реализации:#include# include# include# include"widget . h""gadget . h"<string><vector>struct Widget : : Implstd : : string name ;std : : vector<douЫe> data;Gadget gl, g2 , gЗ;11 В файле "widget . cpp"/ / Как и ранее};Widge t : : Widget ( )11 Согласно разделу 4 .
4: pimpl ( std: : make unique<Impl> O ) 1 1 создаем s td : : unique_ptr_{}51 1 с помощью std : : ma ke_uniqueНепереводимая игра слов, основанная на использовании дпя обычного указателя названия "rawpoiпter" (дословно - "сырой указатель"). - Примеч. пер.4.5. При испоnьзовании идиомы указатеnя на реаnизацию опредеnяйте специаnьные""1 57Вы заметили, что деструктора W i dget больше нет? Дело в том, что нет никакого кода,который требуется в нем разместить. s t d : : unique_pt r автоматически удаляет то, на чтоуказывает, когда он сам (указатель s t d : : unique_ptr ) уничтожается, так что нам не требуется ничего удалять вручную. Это одна из привлекательных сторон интеллектуальныхуказателей: они устраняют необходимость утруждать свои руки вводом кода для освобождения ресурсов вручную.Этот код компилируется, но, увы, это не самый тривиальный клиент!#include "widget .
h"Widget w;1 1 Ошибка !Получаемое вами сообщение об ошибке зависит от используемого компилятора, но в общем случае текст упоминает что-то о применении s i zeof или de lete к неполному типу.Эти операции не входят в число тех, которые можно делать с такими типами.Эта явная неспособность идиомы Pimpl использовать std : : un i que_ptr вызываеттревогу, поскольку ( 1 ) указатели std : : unique_ptr разрекламированы как поддерживающие неполные типы, и (2) идиома Pimpl - один из наиболее распространенных случаевприменения s t d : : unique_ptr.