Сущность технологии СОМ. Библиотека программиста

ОглавлениеДобавить в закладки К обложке

Множественные интерфейсы и имена методов

Множественное наследование является очень эффективной и простой технологией для реализации интерфейсов СОМ в классе C++. Это требует написания очень короткого явного кода, так как большая часть работы компилятора и компоновшика заключается в построении соответствующих СОМ указателей vptr и таблиц vtbl. Если имя метода появляется более чем в одном базовом классе с идентичными типами параметров, то компилятор и компоновщик заполняют каждый элемент vtbl таким образом, чтобы он указывал на одну реализацию метода в классе. Этот режим применяется к таким методам, как QueryInterface, AddRef и Release, так как все интерфейсы СОМ начинаются с этих методов, и все же разработчику класса требуется написать каждый метод только один раз (и это хорошо). Этот же режим применяется и к методам любых интерфейсов, где происходит повтор имени и сигнатуры. Здесь есть одна возможная ловушка множественного наследования.

Иерархия транспортных интерфейсов из этой главы содержит конфликт имен. В интерфейсе ICar (автомобиль) имеется метод, названный GetMaxSpeed (развить максимальную скорость). В интерфейсах IBoat (лодка) и IPlane (самолет) также имеются методы, именуемые GetMaxSpeed с идентичной сигнатурой. Это означает, что при использовании множественного наследования разработчик класса пишет метод GetMaxSpeed один раз, а компилятор и компоновщик инициализируют таблицы vtbl , совместимые с ICar, IBoat и IPlane так, чтобы они указывали только на эту реализацию.

Возможно, это вполне разумное поведение для большого числа реализации. Но что если объекту нужно было вернуть другую максимальную скорость, зависящую от интерфейса, на который был сделан запрос? Поскольку имя и сигнатуры одинаковы, то необходимо принимать неординарные меры для разрешения множественных реализации конфликтного метода. Один из возможных способов состоит в создании промежуточного класса C++, производного от интерфейса и реализующего конфликтный метод путем создания чисто виртуального вызова неконфликтного имени:

struct IXCar : public ICar {

// add new non-clashing method as pure virtual

// добавляем новый неконфликтный метод как чисто виртуальный

virtual HRESULT STDMETHODCALLTYPE GetMaxCarSpeed(long *pval) = 0;

// implement clashing method by upcalling

// non-clashing implementation in derived class

// реализуем конфликтный метод путем вызова

// неконфликтной реализации в производном классе

STDMETHODIMP GetMaxSpeed(long *pval)

{ return GetMaxCarSpeed(pval); }

};

Допуская, что интерфейсы IBoat и IPlane подвергнуты подобной операции, можно реализовывать различные версии GetMaxSpeed простым наследованием от расширенных версий интерфейсов и переопределением неконфликтных версий каждого метода GetMaxSpeed:

class CarBoatPlane: public IXCar, public IXBoat, public IXPlane

{

public:

// Unknown methods – методы IUnknown

STDMETHODIMP QueryInterface(REFIID, void**);

STDMETHODIMP_(ULONG) AddRef(void);

STDMETHODIMP_(ULONG) Release(void);

// IVehicle methods – методы IVehicle

// do not override GetMaxSpeed!

// не подменяем GetMaxSpeed!

// ICar methods – методы ICar

STDMETHODIMP Brake(void);

// IBoat methods – методы IBoat

STDMETHODIMP Sink(void);

// IXPlane methods – методы IXPlane

STDMETHODIMP TakeOff(void);

// upcalled from IXCar::GetMaxSpeed

// вызвано из IXCar::GetMaxSpeed

STDMETHODIMP GetMaxCarSpeed(long *pval);

// upcalled from IXBoat::GetMaxSpeed

// вызвано из IXBoat::GetMaxSpeed

STDMETHODIMP GetMaxBoatSpeed(long *pval);

// called from IXPlane::GetMaxSpeed

// вызвано из IXPlane::GetMaxSpeed

STDMETHODIMP GetMaxPlaneSpeed(long *pval);

}

Рисунок 4.6 иллюстрирует представление этого класса и форматы таблиц vtbl. Отметим, что конфликтный метод GetMaxSpeed не реализован в этом классе. Поскольку каждый из базовых классов CarBoatPlane подменяет этот чисто виртуальный метод, то CarBoatPlane не нуждается в создании своей собственной реализации. Действительно, если бы в CarBoatPlane нужно было подменить GetMaxSpeed, то одна его реализация этого метода подменила бы версии, вызываемые из каждого базового класса, аннулировав результат использования IXCar, IXBoat и IXPlane. В силу этой проблемы данная технология годится только в тех ситуациях, когда можно быть уверенным, что класс реализации (или любые возможные производные классы) никогда не станет подменять конфликтный метод.


Логин
Пароль
Запомнить меня