Объекты классов
Основное требование всех СОМ-классов состоит в том, что они должны иметь
объект класса. Объект класса - это единственный экземпляр (синглетон),
связанный с каждым классом, который реализует функциональность класса,
общую для всех его экземпляров. Объект класса ведет себя как метакласс
по отношению к заданной реализации, а реализуемые им методы выполняют
роль статических функций-членов из C++. По логике вещей, может быть только
один объект класса в каждом классе; однако в силу распределенной природы
СOМ каждый класс может иметь по одному объекту класса на каждую хост-машину
(host machine), на учетную запись пользователя или на процесс, - в зависимости
от того, как используется этот класс. Первой точкой входа в реализацию
класса является ее объект класса.
Объекты класса являются очень полезными программистскими абстракциями.
Объекты класса могут вести себя как известные объекты (когда их идентификатор
CLSID выступает в качестве имени объекта), которые позволяют нескольким
клиентам связываться с одним и тем же объектом, определенным с помощью
данного CLSID. В то время как системы в целом могли быть созданы с использованием
исключительно объектов класса, объекты класса часто используются как посредники
(brokers) при создании новых экземпляров класса или для того, чтобы найти
имеющиеся экземпляры, определенные с помощью какого-нибудь известного
имени объекта. При использовании в этой роли объект класса обычно объявляет
только один или два промежуточных интерфейса, которые позволят клиентам
создать или найти те экземпляры, которые в конечном счете будут выполнять
нужную работу. Например, рассмотрим описанный ранее интерфейс IАре.
Объявление интерфейса IАре не нарушит законы СОМ для объекта
класса:
class GorillaClass : public IApe {
public:
// class objects are singletons, so don't delete
// объекты класса существуют в единственном экземпляре,
// так что не удаляйте их
IMPLEMENT_UNKNOWN_NO_DELETE (GorillaClass)
BEGIN_INTERFACE_TABLE(GorillaClass)
IMPLEMENTS_INTERFACE(IApe)
END_INTERFACE_TABLE()
// IApe methods
// методы IApe
STDMETHODIMP EatBanana(void);
STDMETHODIMP SwingFromTree(void);
STDMETHODIMP get_Weight(long *plbs);
};
Если для данного класса C++ может существовать лишь один экземпляр (так
ведут себя все объекты классов в СОМ), то в любом заданном экземпляре
может быть только одна горилла (gorilla). Для некоторых областей одноэлементных
множеств достаточно. В случае с гориллами, однако, весьма вероятно, что
клиенты могут захотеть создавать приложения, которые будут использовать
несколько различных горилл одновременно. Чтобы обеспечить такое
использование, объект класса не должен экспортировать интерфейс IApe,
а вместо этого должен экспортировать новый интерфейс, который позволит
клиентам создавать новых горилл и/или находить известных горилл по их
имени. Это потребует от разработчика определить два класса C++: один для
реализации объекта класса и другой для реализации действительных экземпляров
класса. Для реализации гориллы класс C++, который определяет экземпляры
гориллы, будет реализовывать интерфейс IApe:
class Gorilla : public IApe {
public:
// Instances are heap-based, so delete when done
// копии размещены в куче, поэтому удаляем после выполнения
IMPLEMENT_UNKNOWN()
BEGIN_INTERFACE_TABLE()
IMPLEMENTS_INTERFACE(IApe)
END_INTERFACE_TABLE()
// IApe methods
// методы IApe
STDMETHODIMP EatBanana(void);
STDMETHODIMP SwingFromTree(void);
STDMETHODIMP get_Weight(long *plbs):
};
Второй интерфейс понадобится для определения тех операций, которые будет
реализовывать объект класса Gorilla:
[object, uuid(753A8AAC-A7FF-11d0-8C30-0080C73925BA)]
interface IApeClass : IUnknown {
HRESULT CreateApe([out, retval] IApe **ppApe);
HRESULT GetApe([in] long nApeID, [out, retval] IApe **ppApe);
[propget] HRESULT AverageWeight([out, retval] long *plbs);
}
Получив это определение интерфейса, объект класса будет реализовывать
методы IApeClass или путем создания новых экземпляров С++-класса
Gorilla (в случае CreateApe), или преобразованием произвольно
выбранного имени объекта (в данном случае типа integer) в отдельный
экземпляр (в случае GetApe):
class GorillaClass : public IApeClass {
public:
IMPLEMENT_UNKNOWN_NO_DELETE(GorillaClass)
BEGIN_INTERFACE_TABLE(GorillaClass)
IMPLEMENTS_INTERFACE(IApeClass)
END_INTERFACE_TABLE()
STDMETHODIMP CreateApe(Ape **ppApe) {
if ((*ppApe = new Gorilla) == 0)
return E_OUTOFMEMORY;
(*ppApe)->AddRef();
return S_OK;
}
STDMETHODIMP GetApe(long nApeID, IApe **ppApe) {
// assume that a table of well-known gorillas is
// being maintained somewhere else
// допустим, что таблица для известных горилл
// поддерживается где-нибудь еще
extern Gorilla *g_rgWellKnownGorillas[];
extern int g_nMaxGorillas;
// assert that nApeID is a valid index
// объявляем, что nApeID - допустимый индекс
*ррАре = 0;
if (nApeID > g_nMaxGorillas || nApeID < 0)
return E_INVALIDARG;
// assume that the ID is simply the index into the table
// допустим, что ID - просто индекс в таблице
if ((*ppApe = g_rgWellKnownGorillas[nApeID]) == 0)
return E_INVALIDARG;
(*ppApe)->AddRef();
return S_OK;
}
STDMETHODIMP get_AverageWeight(long *plbs) {
extern *g_rgWellKnownGorillas[];
extern int g_nMaxGorillas;
*plbs = 0;
long lbs;
for (int i = 0; i < g_nMaxGorillas; i++) {
g_rgWellKnownGorillas[i]->get_Weight(&lbs);
*plbs += lbs;
}
// assumes g_nMaxGorillas is non-zero
// предполагается, что g_nMaxGorillas ненулевой
*plbs /= g_nMaxGorillas;
return S_OK;
}
};
Отметим, что в этом коде предполагается, что внешняя таблица известных
горилл уже поддерживается - или самими копиями Gorilla, или каким-нибудь
другим посредником (agent).
Активация
Использование SCM
Классы и серверы
Обобщения
Оптимизации
Снова интерфейс и реализация
Моникеры и композиция
Моникеры и сохраняемость
Время жизни сервера
Классы и IDL
Эмуляция классов
Категории компонентов
Где мы находимся?
|