Сущность технологии COM

       

Объекты классов


Основное требование всех СОМ-классов состоит в том, что они должны иметь объект класса. Объект класса — это единственный экземпляр (синглетон), связанный с каждым классом, который реализует функциональность класса, общую для всех его экземпляров. Объект класса ведет себя как метакласс по отношению к заданной реализации, а реализуемые им методы выполняют роль статических функций-членов из 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).


Содержание раздела