"Сущность технологии СОМ. Библиотека программиста" - читать интересную книгу автора (Бокс Дональд)Управление ресурсамиЕще одна проблема поддержки нескольких интерфейсов из одного объекта становится яснее, если исследовать схему использования клиентом метода void f(void) { IFastString *pfs = 0; IPersistentObject *ppo = 0; pfs = CreateFastString(«Feed BOB»); if (pfs) { ppo = (IPersistentObject *) pfs-gt;Dynami else { ppo-gt;Save(«C:\\autoexec.bat»); ppo-gt;Delete(); } } } Хотя вначале объект был связан через свой интерфейс Простейшее решение этой проблемы – ввести в каждый объект счетчик ссылок, который увеличивается, когда указатель интерфейса дублируется, и уменьшается, когда указатель интерфейса уничтожается. Это предполагает изменение определения class IExtensibleObject { public: virtual void *DynamicCast (const char* pszType) =0; virtual void Delete(void) = 0; }; на class IExtensibleObject { public: virtual void *DynamicCast(const char* pszType) = 0; virtual void DuplicatePointer(void) = 0; virtual void DestroyPointer(void) = 0; }; Разместив эти методы, все пользователи 1) Когда указатель интерфейса дублируется, требуется вызов 2) Когда указатель интерфейса более не используется, следует вызвать Эти методы могут быть реализованы в каждом объекте: нужно просто фиксировать количество действующих указателей и уничтожать объект, когда невыполненных указателей не осталось: class FastString : public IFastString, public IPersistentObject { int mcPtrs; // count of outstanding ptrs // счетчик невыполненных указателей public: // initialize pointer count to zero // сбросить счетчик указателя в нуль FastString(const char *psz) : mcPtrs(0) { } void DuplicatePointer(void) { // note duplication of pointer // отметить дублирование указателя ++mcPtrs; } void DestroyPointer(void) { // destroy object when last pointer destroyed // уничтожить объект, когда уничтожен последний указатель if (-mcPtrs == 0) delete this; } : : : }; Этот совершенно стандартный код мог бы просто быть включен в базовый класс или в макрос С-препроцессора, чтобы его могли использовать все реализации. Чтобы поддерживать эти методы, все программы, которые манипулируют или управляют указателями интерфейса, должны придерживаться двух простых правил IFastString* CreateFastString(const char *psz) { IFastString *pfsResult = new FastString(psz); if (pfsResult) pfsResult-gt;DuplicatePointer(); return pfsResult; } Реализация копирует указатель и в другом месте – в методе Dynamic_Cast: void *FastString::Dynamic_Cast(const char *pszType) { void *pvResult = 0; if (strcmp(pszType, «IFastString») == 0) pvResult = static_castlt;IFastString*gt;(this); else if (strcmp(pszType, «IPersistentObject») == 0) pvResult = static_castlt;IPersistentObject*gt;(this); else if (strcmp(pszType, «IExtensibleObject») == 0) pvResult = static_castlt;IFastString*gt;(this); else return 0; // request for unsupported interface // запрос на неподдерживаемый интерфейс // pvResult now contains a duplicated pointer, so // we must call DuplicatePointer prior to returning // теперь pvResult содержит скопированный указатель, // поэтому нужно перед возвратом вызвать DuplicatePointer ((IExtensibleObject*)pvResult)-gt;DuplicatePo1nter(); return pvResult; } С этими двумя усовершенствованиями соответствующий код пользователя становится значительно более однородным и прозрачным: void f(void) { IFastString *pfs = 0; IPersistentObject *ppo = 0; pfs = CreateFastString(«Feed BOB»); if (pts) { рро = (IPersistentObject *) pfs-gt;DynamicCast(«IPersistentObject»); if (ppo) { ppo-gt;Save(«C:\\autoexec.bat»); ppo-gt;DestroyPointer(); } pfs-gt;DestroyPointer(); } } Поскольку каждый указатель теперь трактуется как автономный объект с точки зрения времени жизни, клиенту можно не интересоваться тем, какой указатель соответствует какому объекту. Вместо этого клиент просто придерживается двух простых правил и предоставляет объектам самим управлять своим временем жизни. При желании способ вызова Использование этой схемы вычисления ссылок позволяет объекту весьма единообразно выставлять множественные интерфейсы. Возможность выставления нескольких интерфейсов из одного класса реализации позволяет типу данных участвовать в различных контекстах. Например, новая постоянная подсистема могла бы определить собственный интерфейс для управления автозагрузкой и автозаписью объектов на некоторый специализированный носитель. Класс |
||
|