Сущность технологии СОМ. Библиотека программиста
Шрифт:
if (!ppo) pfs->Delete;
else { ppo->Save(«C:\\autoexec.bat»);
ppo->Delete; }
}
}
Хотя вначале объект был связан через свой интерфейс IFastString , клиентский код вызывает метод Delete через интерфейс IPersistentObject. С использованием свойства C++ о множественном наследовании это вполне допустимо, так как все таблицы vtbl , порожденные классом IExtensibleObject,
Простейшее решение этой проблемы – ввести в каждый объект счетчик ссылок, который увеличивается, когда указатель интерфейса дублируется, и уменьшается, когда указатель интерфейса уничтожается. Это предполагает изменение определения IExtensibleObject с
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;
};
Разместив эти методы, все пользователи IExtensibleObject должны теперь придерживаться следующих двух соображений:
1) Когда указатель интерфейса дублируется, требуется вызов DuplicatePointer.
2) Когда указатель интерфейса более не используется, следует вызвать DestroyPointer.
Эти методы могут быть реализованы в каждом объекте: нужно просто фиксировать количество действующих указателей и уничтожать объект, когда невыполненных указателей не осталось:
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;
}
: : :
};
Этот совершенно стандартный код мог бы просто быть включен в базовый класс или в макрос С-препроцессора, чтобы его могли использовать все реализации.
Чтобы поддерживать эти методы, все программы, которые манипулируют или управляют указателями интерфейса, должны придерживаться двух простых правил DuplicatePointer/DestroyPointer. Для реализации FastString это означает модификацию двух функций. Функция CreateFastString берет начальный указатель, возвращаемый новым оператором C++, и копирует его в стек для возврата клиенту. Следовательно, необходим вызов DuplicatePointer:
IFastString* CreateFastString(const char *psz)
{
IFastString *pfsResult = new FastString(psz);
if (pfsResult) pfsResult->DuplicatePointer;
return pfsResult;
}
Реализация копирует указатель и в другом месте – в методе Dynamic_Cast:
void *FastString::Dynamic_Cast(const char *pszType)
{
void *pvResult = 0;
if (strcmp(pszType, «IFastString») == 0) pvResult = static_cast<IFastString*>(this);
else if (strcmp(pszType, «IPersistentObject») == 0) pvResult = static_cast<IPersistentObject*>(this);
else if (strcmp(pszType, «IExtensibleObject») == 0) pvResult = static_cast<IFastString*>(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)->DuplicatePo1nter;
return pvResult;
}
С этими двумя усовершенствованиями соответствующий код пользователя становится значительно более однородным и прозрачным:
void f(void)
{
IFastString *pfs = 0;
IPersistentObject *ppo = 0;
pfs = CreateFastString(«Feed BOB»);
if (pts) {
рро = (IPersistentObject *) pfs->DynamicCast(«IPersistentObject»);
if (ppo) { ppo->Save(«C:\\autoexec.bat»);
ppo->DestroyPointer; }
pfs->DestroyPointer; }
}
Поскольку каждый указатель теперь трактуется как автономный объект с точки зрения времени жизни, клиенту можно не интересоваться тем, какой указатель соответствует какому объекту. Вместо этого клиент просто придерживается двух простых правил и предоставляет объектам самим управлять своим временем жизни. При желании способ вызова DuplicatePointer и DestroyPointer можно легко скрыть за интеллектуальным указателем (smart pointer) C++.