Анимация
JavaScript
|
Главная Библионтека к интеллектуальным указателям нельзя применять стратегию захвата на уровне объектов, поскольку операция копирования манипулирует сразу тремя объектами: текущий объект, подлежащий вставке или удалению, предыдущий объект и следующий объект в списке владения. Если все же возникает необходимость реализовать захват на уровне объектов, следует убедиться, что каждому объекту соответствует один мьютекс, поскольку для каждого объекта существует отдельный список владения. Мьютексы для каждого объекта можно разместить в динамической памяти, хотя это сводит к нулю все преимущества связывания ссылок над их подсчетом. Связывание ссылок привлекательно именно потому, что оно не использует динамическую память. В качестве альтернативы можно использовать стратегию внедрения: мьютекс хранится в объекте, на который ссылается интеллектуальный указатель. Однако существование разумной и эффективной альтернативы - интеллектуальных указателей с подсчетом ссылок - вынуждает отказаться от реализации этой стратегии. Итак, интеллектуальные указатели, использующие подсчет ссылок или их связывание, возникают в многопоточной среде. Для реализации безопасной стратегии подсчета ссылок необходимы атомарные операции. Для реализации безопасной стратегии связывания ссылок нужны мьютексы. Класс SmartPtr реализует только безопасную стратегию подсчета ссылок. 7.14. Сборка До сих пор мы рассматривали каждый вопрос по отдельности. Настало время собрать все рещения воедино и воплотить их в реализации класса SmartPtr. Мы будем по-прежнему следовать принципам разработки классов на основе стратегий, описанным в главе 1. Каждый аспект проектирования, не имеющий единственного рещения, реализуется в классах стратегий. Шаблонный класс SmartPtr получает каждую стратегию в виде отдельного щаблонного параметра, наследует все эти щаблонные параметры, позволяя соответствующим стратегиям сохранять состояние. Вернемся мысленно к предыдущим разделам, перечисляя основные аспекты класса SmartPtr, каждый из которых представляет собой отдельную стратегию. • Стратегия Storage (раздел 7.3). По умолчанию сохраняемым типом является тип т* (тип т - это первый шаблонный параметр класса SmartPtr), типом указателей - тоже т*, а ссылочным типом - т&. Объект, на который ссылается интеллектуальный указатель, уничтожается оператором delete. • Стратегия Ownership (раздел 7.5). Обычно реализуется с помощью глубокого копирования, подсчета ссылок, связывания ссылок и разрушающего копирования. Обратите внимание на то, что стратегия Ownership не связана с механизмом уничтожения объектов, который относиЛя к стратегии Storage. Стратегия Ownership лишь указывает момент уничтожения объекта. • Стратегия Conversion (раздел 7.7). В некоторых приложениях необходимо преобразовывать интеллектуальные указатели в обычные. Обратное преобразование не допускается. • Стратегия Checking (раздел 7.10). Эта стратегия проверяет правильность инициализации и разыменования класса SmartPtr. Остальные свойства не настолько важны, чтобы создавать для них отдельные стратегии. • Оператор взятия адреса (раздел 7.6) лучше не перегружать. • Проверка равенства и неравенства выполняется с помощью приема, описанного в разделе 7.8. • Отнощения порядка (раздел 7.9) остаются нереализованными. Однако в библиотеке Loki осуществляется специализация функции std: :less для объектов кЛасса SmartPtr. Пользователь может определить оператор <, а библиотека Loki поможет выразить через него остальные операторы сравнения. • В библиотеке Loki определена корректная реализация константных объектов класса SmartPtr, объектов, на которые ссылаются интеллектуальные указатели, а также их обоих одновременно. • Массивь[ специально не предусматриваются, однако одна из готовых реализаций стратегии Storage может удалять массивы с помощью оператора delete[]. Каждый аспект реализации класса SmartPtr рассматривался отдельно от остальных. Это позволяет лучше разобраться в их механизмах. Намного полезнее разобрать реализацию на части и рассмотреть их по отдельности, чем изучать их в комплексе. Разделяй и властвуй - этот старый девиз Юлия Цезаря вполне пригоден для разработки интеллектуальных указателей. (Бьюсь об заклад, он этого не предвидел!) Мы разбиваем проблемы на небольшие составные классы, называемые стратегиями (policy). Каждая стратегия связана только с одним аспектом реализации класса. Класс SmartPtr наследует все эти классы, одновременно приобретая все их свойства. Это очень простой и невероятно гибкий механизм. Кроме того, каждая стратегия задается одним шаблонным параметром. Это позволят смешивать и сопоставлять существующие классы стратегий, а также создавать на их основе свои собственные стратегии. Первым идет тип объекта, на который ссылается интеллектуальный указатель, за ним - все стратегии. В итоге получается следующее объявление класса SmartPtr. template < typename т, template <class> class OwnershipPolicy = RefCounted, class ConversionPolicy = DisallowConversion, template <class> class CheckingPolicy = AssertCheck, template <class> class storagePolicy = DefaultSPStorage > class SmartPtr; Сначала следует указывать стратегии, которые настраиваются чаще других. Ниже рассматриваются требования, предъявляемые к четырем стратегиям, определенным ранее. Все эти стратегии должны иметь семантику значений, т.е. они должны определять соответствующие конструктор копирования и оператор присваивания. 7.14.1. Многопоточность на уровне объектов Стратегия storage абстрагирует структуру интеллектуального указателя. Она осуществляет определение типов и хранит фактический объект pointee . Если класс storageimpl является реализацией стратегии storage, а storageimpl - это объект типа Storageimpl <т>, то применяются конструкции, указанные в табл. 7.1. Реализация стратегии Storage по умолчанию имеет следующий вид. template <class т> class DefaultSPStorage { protected: typedef т* storedType; тип объекта pointee typedef т* PointerType; тип объекта, возвращаемого оператором -> typedef т& ReferenceType; тип объекта, возвращаемого оператором * public: DefaultSPStorageC) : pointee (Default()) DefaultSPStorageCconst StoredType* p): pointee (p) {} PointerType operator->() const { return pointee ; } ReferenceType operator*() const { return *pointee ; } • friend inline PointerType Getlmpl(const DefaultSPStorage& sp) { return sp.pointee ; } friend inline const StoredType* GetlmplRef( const DefaultSpStorage& sp) { return sp.pointee ; } friend inline StoredType* GetlmplRef(DefaultSPStorage& sp) { return sp.pointee ; } protected: void DestroyO { delete pointee ; } static StoredType Default() { return 0; } private: StoredType pointee ; Кроме класса DefaultSPStorage в библиотеке Loki определены следующие классы. • Класс Arraystorage, использующий оператор delete[] внутри функции Release. • Класс LockedStorage, использующий иерархическую реализацию интеллектуального класса, захватывающего данные при разыменовании (раздел 7.13.1). • Класс HeapStorage, использующий явный вызов деструктора и функцию std:: free для освобождения данных. Таблица 7.1. Конструкции стратегии Storage Выражение Семантика Storagelmpl<т>::StoredType Тип, фактически хранимый реализацией. По умолчанию: т* Storagelmpl <т>: : PointerType Тип указателей, определенный реализацией. Возвращается оператором ->, определенным в классе SmartPtr. По умолчанию: Т*. Может отличаться от типа Storagelmpl <Т>:: StoredType, если используется иерархия интеллектуальных указателей (разделы 73 и 7.13.1) Storagelmpl<т>: :ReferenceType Ссылочный тип. Возвращается оператором * класса SmartPtr. По умолчанию: Т& Getlmpl (storagelmpl) Возвращает объект типа Storagelmpl<т>::StoredType GetlmplRef (storagelmpl) Возвращает объект типа Storagelmpl<т>::StoredType* (если объект storagelmpl является константным, конструкция объявляется константной) 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 [ 64 ] 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |