Основное требование всех СОМ-классов состоит в том, что они должны иметь объект класса. Объект класса – это единственный экземпляр (синглетон), связанный с каждым классом, который реализует функциональность класса, общую для всех его экземпляров. Объект класса ведет себя как метакласс по отношению к заданной реализации, а реализуемые им методы выполняют роль статических функций-членов из C++. По логике вещей, может быть только один объект класса в каждом классе; однако в силу распределенной природы СOМ каждый класс может иметь по одному объекту класса на каждую хост-машину (host machine), на учетную запись пользователя или на процесс, – в зависимости от того, как используется этот класс. Первой точкой входа в реализацию класса является ее объект класса.
Объекты класса являются очень полезными программистскими абстракциями. Объекты класса могут вести себя как известные объекты (когда их идентификатор CLSID выступает в качестве имени объекта), которые позволяют нескольким клиентам связываться с одним и тем же объектом, определенным с помощью данного CLSID. В то время как системы в целом могли быть созданы с использованием исключительно объектов класса, объекты класса часто используются как посредники (brokers) при создании новых экземпляров класса или для того, чтобы найти имеющиеся экземпляры, определенные с помощью какого-нибудь известного имени объекта. При использовании в этой роли объект класса обычно объявляет только один или два промежуточных интерфейса, которые позволят клиентам создать или найти те экземпляры, которые в конечном счете будут выполнять нужную работу. Например, рассмотрим описанный ранее интерфейс IАре . Объявление интерфейса IАре не нарушит законы СОМ для объекта класса:
class GorillaClass : public IApe
{
public:
// class objects are singletons, so don't delete
// объекты класса существуют в единственном экземпляре,
// так что не удаляйте их
IMPLEMEN TUNKNOWN N ODELETE (GorillaClass)
BEGIN INTERFAC ETABLE(GorillaClass)
IMPLEMENTS INTERFACE(IApe)
EN DINTERFACE TABLE()
// IApe methods
// методы IApe
STDMETHODIMP EatBanana(void);
STDMETHODIMP SwingFromTree(void);
STDMETHODIMP ge tWeight(long *plbs);
};
Если для данного класса C++ может существовать лишь один экземпляр (так ведут себя все объекты классов в СОМ), то в любом заданном экземпляре может быть только одна горилла (gorilla). Для некоторых областей одноэлементных множеств достаточно. В случае с гориллами, однако, весьма вероятно, что клиенты могут захотеть создавать приложения, которые будут использовать несколько различных горилл одновременно. Чтобы обеспечить такое использование, объект класса не должен экспортировать интерфейс IApe , а вместо этого должен экспортировать новый интерфейс, который позволит клиентам создавать новых горилл и/или находить известных горилл по их имени. Это потребует от разработчика определить два класса C++: один для реализации объекта класса и другой для реализации действительных экземпляров класса. Для реализации гориллы класс C++, который определяет экземпляры гориллы, будет реализовывать интерфейс IApe :
class Gorilla : public IApe
{
public:
// Instances are heap-based, so delete when done
// копии размещены в куче, поэтому удаляем после выполнения
IMPLEMEN TUNKNOWN()
BEGIN INTERFAC ETABLE()
IMPLEMENTS INTERFACE(IApe)
EN DINTERFACE TABLE()
// IApe methods
// методы IApe
STDMETHODIMP EatBanana(void);
STDMETHODIMP SwingFromTree(void);
STDMETHODIMP ge tWeight(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: IMPLEMEN TUNKNOWN N ODELETE(GorillaClass)
BEGIN INTERFAC ETABLE(GorillaClass)
IMPLEMENTS INTERFACE(IApeClass)
EN DINTERFACE TABLE()
STDMETHODIMP CreateApe(Ape **ppApe)
{
if ((*ppApe = new Gorilla) == 0) return EOUTOFMEMORY;
(*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 * grgWellKnownGorillas[];
extern int g nMaxGorillas;
// assert that nApeID is a valid index
// объявляем, что nApeID – допустимый индекс
*ррАре = 0;
if (nApeID > gnMaxGorillas || nApeID < 0) return E INVALIDARG;
// assume that the ID is simply the index into the table
// допустим, что ID – просто индекс в таблице
if ((*ppApe = grgWellKnownGorillas[nApeID]) == 0) return E INVALIDARG;
(*ppApe)->AddRef();
return SOK;
}
STDMETHODIMP ge tAverageWeight(long *plbs)
{
extern *g rgWellKnownGorillas[];
extern int gnMaxGorillas;
*plbs = 0;
long lbs;
for (int i = 0; i < g nMaxGorillas; i++)
{
grgWellKnownGorillas[i]->get Weight(&lbs);
*plbs += lbs;
}
// assumes gnMaxGorillas is non-zero
// предполагается , что gnMaxGorillas ненулевой
*plbs /= g nMaxGorillas;
return SOK;
}
};
Отметим, что в этом коде предполагается, что внешняя таблица известных горилл уже поддерживается – или самими копиями Gorilla , или каким-нибудь другим посредником (agent). |