- Фантастика и фэнтези
- Ироническое фэнтези
- Стимпанк
- Детективная фантастика
- Киберпанк
- Попаданцы
- LitRPG
- Космоопера
- Технофэнтези
- Городская фантастика
- Русское фэнтези
- Юмористическая фантастика
- Космическая фантастика
- Боевое фэнтези
- Эпическая фантастика
- Зарубежная фантастика
- Книги магов
- Мистика
- Разная фантастика
- Разное фэнтези
- Любовное фэнтези
- Романтическая фантастика
- Городское фентези
- Историческая фантастика
- Историческое фэнтези
- Социально-философская фантастика
- Сказочная фантастика
- Иностранное фэнтези
- Романтическое фэнтези
- Ироническая фантастика
- Ужасы и Мистика
- Постапокалипсис
- Героическая фантастика
- Фэнтези
- Научная Фантастика
- Социально-психологическая
- Альтернативная история
- Боевая фантастика
- Разная литература
- Пословицы, поговорки
- Шахматы
- Визуальные искусства
- Фанфик
- Авто и ПДД
- Военное
- Отраслевые издания
- Гиды, путеводители
- Зарубежная образовательная литература
- Военная история
- Цитаты из афоризмов
- Современная литература
- Великолепные истории
- Начинающие авторы
- Современная зарубежная литература
- Боевые искусства
- Изобразительное искусство, фотография
- Кино
- Литература 19 века
- Недвижимость
- Музыка, музыканты
- Готические новеллы
- Истории из жизни
- Культура и искусство
- Спецслужбы
- Зарубежная прикладная литература
- Музыка, танцы
- Подростковая литература
- Прочее
- Военная техника, оружие
- Газеты и журналы
- Периодические издания
- Домоводство, Дом и семья
- Любовные романы
- Экономическая литература
- Научные и научно-популярные книги
- О животных
- Биохимия
- Культурология
- Юриспруденция
- Политика
- Психотерапия
- Языкознание
- Научпоп
- Медицина
- Психология, личное
- Радиотехника
- Филология
- Педагогика
- Образовательная литература
- Деловая литература
- Физика
- Науки: разное
- Химия
- Воспитание детей, педагогика
- Детская психология
- Зарубежная публицистика
- Биология
- Архитектура
- Зарубежная психология
- Науки о космосе
- Социология
- Математика
- География
- Беременность, ожидание детей
- Литературоведение
- Транспорт, военная техника
- Обществознание
- Зоология
- География
- Альтернативная медицина
- Иностранные языки
- Ветеринария
- Рефераты
- Астрология
- Биофизика
- Экология
- История Европы
- Учебники
- Шпаргалки
- Государство и право
- Ботаника
- Религиоведение
- История
- Техническая литература
- Прочая научная литература
- Психология
- Детективы и Триллеры
- Справочная литература
- Проза
- Новелла
- Юмор
- Проза
- Историческая проза
- Русская современная проза
- Советская классическая проза
- Повести
- Очерки
- Эссе
- Рассказы
- Зарубежная современная проза
- Сентиментальная проза
- Зарубежная классика
- Афоризмы
- Эпистолярная проза
- Феерия
- Семейный роман/Семейная сага
- Разное
- Антисоветская литература
- Магический реализм
- Русская классическая проза
- Современная проза
- О войне
- Контркультура
- Классическая проза
- Поэзия, Драматургия
- Детская литература
- Детская фантастика
- Детские остросюжетные
- Детские стихи
- Детские приключения
- Внеклассное чтение
- Учебная литература
- Зарубежные детские книги
- Бизнес
- Загадки
- Книги для подростков
- Школьные учебники
- Детский фольклор
- Книги для дошкольников
- Детские детективы
- Детская познавательная и развивающая литература
- Буквари
- Прочая детская литература
- Сказка
- Детская проза
- Детская образовательная литература
- Юмор
- Документальные книги
- Бизнес
- Внешнеэкономическая деятельность
- Тайм-менеджмент
- Кадровый менеджмент
- О бизнесе популярно
- Управление, подбор персонала
- Бизнес
- Менеджмент и кадры
- Продажи
- Экономика
- Ценные бумаги и инвестиции
- Малый бизнес
- Делопроизводство, офис
- Корпоративная культура, бизнес
- Банковское дело
- Финансы
- Зарубежная деловая литература
- Ораторское искусство / риторика
- Личные финансы
- Интернет-бизнес
- Поиск работы
- Государственное и муниципальное управление
- Бухучет и аудит
- Работа с клиентами
- Менеджмент
- Краткое содержание
- Личная эффективность
- Переговоры
- Приключения
- Старинная литература
- Религия и духовность
- Компьютеры и Интернет
- Блог
Читаем без скачивания Сущность технологии СОМ. Библиотека программиста - Дональд Бокс
Шрифт:
Интервал:
Закладка:
// (book-specific header file)
// (заголовочный файл, специфический для данной книги)
#define COMPOSITE_OFFSET(ClassName, BaseName,
MemberType, MemberName)
(DWORD(static_cast<BaseName*>(
reinterpret_cast<MemberType*>(0x10000000 +
offsetof(ClassName, MemberName)))) – 0х10000000)
#define IMPLEMENTS_INTERFACE_WITH_COMPOSITE(Req,
MemberType, MemberName)
{ &IID_##Req,ENTRY_IS_OFFSET, COMPOSITE_OFFSET(_IT,
Req, MemberType, MemberName) },
// impunk.h
// (book-specific header file)
// (заголовочный файл, специфический для данной книги)
#define IMPLEMENT_COMPOSITE_UNKNOWN(OuterClassName,
InnerClassName, DataMemberName)
OuterClassName *This()
{ return (OuterClassName*)((char*)this –
offsetof(OuterClassName, DataMemberName)); }
STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
{ return This()->QueryInterface(riid, ppv); }
STDMETHODIMP_(ULONG) AddRef(void)
{ return This()->AddRef(); }
STDMETHODIMP_(ULONG) Release(void)
{ return This()->Release(); }
Эти макросы препроцессора просто дублируют фактические реализации QueryInterface, AddRef и Release , использованные в композиции.
Динамическая композиция
Если для реализации интерфейса в классе C++ используется множественное наследование или композиция, то в каждом объекте этого класса будут содержаться служебные данные (overhead) указателя vptr размером в четыре байта на каждый поддерживаемый интерфейс (принимая, что sizeof (void*) == 4). Если число интерфейсов, экспортируемых объектом, невелико, то эти служебные данные не играют важной роли, особенно в свете преимуществ, предоставляемых программной моделью СОМ. Если, однако, число поддерживаемых интерфейсов велико, то размер служебных данных vptr может вырасти до такой степени, что часть объекта, не связанная с СОМ, будет казаться маленькой по сравнению с ними. При использовании каждого из этих интерфейсов все время без служебных данных не обойтись. Если же, однако, эти интерфейсы не будут использоваться никогда или использоваться в течение короткого времени, то можно воспользоваться лазейкой в Спецификации СОМ и оптимизировать vptr некоторых неиспользуемых объектов.
Вспомним правило, гласящее, что все запросы QueryInterface на объект относительно IUnknown должны возвращать точно такое же значение указателя. Именно так в СОМ обеспечивается идентификация объектов. В то же время Спецификация СОМ определенно разрешает возвращать другие значения указателей в ответ на запросы QueryInterface относительно любых других типов интерфейсов, кроме IUnknown. Это означает, что для нечасто используемых интерфейсов объект может динамически выделять память для vptr по требованию, не заботясь о возврате того же самого динамически выделенного блока памяти каждый раз, когда запрашивается какой-либо интерфейс. Эта технология временного (transient) размещения композитов впервые была описана в «белой книге» Microsoft Поваренная книга для программистов СОМ (Microsoft white paper The СОМ Programmer's Cookbook), написанной Криспином Госвеллом (Crispin Goswell) (http://www.microsoft.com/oledev). В этой «белой книге» такие временные интерфейсы называются отделяемыми (tearoff).
Реализация отделяемого интерфейса подобна реализации интерфейса с использованием композиции. Для отделяемого интерфейса должен быть определен второй класс, наследующий тому интерфейсу, который он будет реализовывать. Чтобы обеспечить идентификацию, QueryInterface отделяемого интерфейса должен делегировать управление функции QueryInterface основного класса. Два основных различия заключаются в том, что:
1) главный объект динамически размещает отделяемый интерфейс вместо того, чтобы иметь элемент данных экземпляра, и
2) отделяемый композит должен содержать явный обратный указатель на главный объект, так как технология фиксированного смещения, используемая в композиции, здесь не работает, поскольку отделяемый интерфейс изолирован от основного объекта. Следующий класс реализует IBoat как отделяемый интерфейс:
class CarBoat : public ICar
{
LONG m_cRef;
CarBoat (void): m_cRef(0) {}
public:
// IUnknown methods
// методы IUnknown
STDMETHODIMP QueryInterface(REFIID, void**);
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);
// IVehicle methods
// методы IVehicle
STDMETHODIMP GetMaxSpeed(long *pMax);
// ICar methods
// методы ICar
STDMETHODIMP Brake(void);
// define nested class that implements IBoat
// определяем вложенный класс, реализующий IBoat
struct XBoat : public IBoat
{
LONG m_cBoatRef;
// back pointer to main object is explicit member
// обратный указатель на главный объект – явный член
CarBoat *m_pThis;
inline CarBoat* This()
{
return m_pThis;
}
XBoat(CarBoat *pThis);
~XBoat(void);
STDMETHODIMP QueryInterface(REFIID, void**);
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);
STDMETHODIMP GetMaxSpeed(long *pval);
STDMETHODIMP Sink(void);
};
// note: no data member of type Xboat
// заметим: нет элементов данных типа Xboat
};
Для QueryInterface главного объекта необходимо динамически разместить новый отделяемый интерфейс – каждый раз, когда запрашивается IBoat:
STDMETHODIMP CarBoat::QueryInterface(REFIID riid, void **ppv)
{
if (riid == IID_IBoat)
*ppv = static_cast<IBoat*>(new XBoat(this));
else if (riid == IID_IUnknown)
*ppv = static_cast<IUnknown*>(this);
:
:
:
Каждый раз при получении запроса на интерфейс IBoat размещается новый отделяемый интерфейс. Согласно стандартной практике QueryInterface вызова AddRef посредством результирующего указателя: ((IUnknown*)*ppv)->AddRef();
AddRef будет обрабатывать непосредственно из QueryInterface только отделяемый интерфейс. Важно то, что главный объект остается в памяти столько времени, сколько существует отделяемый интерфейс. Простейший путь обеспечить это – заставить сам отделяемый интерфейс представлять неосвобожденную ссылку. Это можно реализовать в разработчике и деструкторе отделяемого интерфейса:
CarBoat::XBoat::XBoat(CarBoat *pThis) : m_cBoatRef(0), m_pThis(pThis)
{
m_pThis->AddRef();
}
CarBoat::XBoat::~XBoat(void)
{
m_pThis->Release();
}
Как и в случае с композицией, методу QueryInterface отделяемого интерфейса требуется идентифицировать объект, делегируя освобождение функции главного объекта. Однако отделяемый интерфейс может выявлять запросы на тот интерфейс (интерфейсы), который он сам реализует, и просто возвращать указатель, обработанный AddRef, себе самому:
STDMETHODIMP CarBoat::XBoat::QueryInterface(REFIID riid, void**ppv)
{
if (riid != IID_IBoat) return This()->QueryInterface(riid, ppv);
*ppv = static_cast<IBoat*>(this);
reinterpret_cast<IUnknown*>(*ppv)->AddRef();
return S_OK;
}
Ввиду того, что отделяемый интерфейс должен самоуничтожаться, когда он больше не нужен, он должен поддерживать свой собственный счетчик ссылок и уничтожать себя, когда этот счетчик достигнет нуля. Как отмечалось ранее, деструктор отделяемого интерфейса освободит главный объект до того, как сам исчезнет из памяти:
STDMETHODIMP_(ULONG) CarBoat::XBoat::AddRef (void)
{
return InterlockedIncrement(&m_cRef);
}
STDMETHODIMP_(ULONG) CarBoat::XBoat::Release(void)
{
ULONG res = InterlockedDecrement(&m_cBoatRef);
if (res == 0) delete this;
// dtor releases main object
// деструктор освобождает главный объект
return res;
}
Как и в случае с композицией, метод This() можно использовать в любых методах отделяемого интерфейса, которым требуется получить статус главного объекта. Разница состоит в том, что отделяемым интерфейсам требуется явный обратный указатель, в то время как нормальные композиты могут использовать фиксированные смещения, выделяя по четыре байта на композит.
На первый взгляд, отделяемые интерфейсы кажутся лучшей из всех возможностей. Когда интерфейс не используется, то на его служебные данные отводится нуль байт объекта. Когда же интерфейс используется, объект косвенно тратит 4 байта на служебные данные отделяемого интерфейса. Подобное впечатление базируется на нескольких обманчивых предположениях. Во-первых, затраты на работающий отделяемый интерфейс составляют отнюдь не только 4 байта памяти для его vptr. Отделяемому интерфейсу требуются также обратный указатель и счетчик ссылок[1]. Во-вторых, несмотря на возможность использования специального распределителя памяти (custom memory allocator ), отделяемому интерфейсу потребуется по крайней мере 4 дополнительных байта на выравнивание и/или заполнение заголовков динамически выделенной памяти, используемых С-библиотекой для реализации malloc/operator new . Это означает, что объект действительно экономит 4 байта, когда интерфейс не используется. Но когда интерфейс используется, отделяемый интерфейс тратит как минимум 12 байт, если подключен специальный распределитель памяти, и 16 байт, если, по умолчанию, подключен оператор new. Если интерфейс запрашивается редко, то такая оптимизация имеет смысл, особенно если клиент освобождает этот интерфейс вскоре после получения. Если же клиент хранит отделяемый интерфейс в течение всего времени жизни объекта, то преимущества отделяемого интерфейса теряются.
К сожалению, дело с отделяемым интерфейсом обстоит еще хуже. Как видно из показанной ранее реализации, если объект получает два запроса QueryInterface на тот же самый отделяемый интерфейс, то будут созданы две копии этого отделяемого интерфейса, так как указатель на первый из них полностью забывается главным объектом, поскольку он был возвращен вызывающему объекту. Это означает, что в этом случае отделяемый интерфейс занимает по крайней мере от 24 до 32 байт, так как в памяти находятся оба vptr отделяемого интерфейса, по одному на каждый запрос QueryInterface. Эта память не будет восстановлена, пока клиент не освободит каждый отделяемый интерфейс. Ситуация, когда два запроса QueryInterface удерживают указатель в течение всего времени жизни объекта, особенно важна, так как именно это и происходит при удаленном обращении к объекту. СОМ-слой, реализующий удаленные вызовы, будет дважды запрашивать объект (с помощью QueryInterface) на предмет одного и того же интерфейса и будет удерживать оба результата в течение всего времени жизни объекта. Это обстоятельство делает отделяемые интерфейсы особенно рискованными для объектов, к которым может осуществляться удаленный доступ.