- Фантастика и фэнтези
- Ироническое фэнтези
- Стимпанк
- Детективная фантастика
- Киберпанк
- Попаданцы
- LitRPG
- Космоопера
- Технофэнтези
- Городская фантастика
- Русское фэнтези
- Юмористическая фантастика
- Космическая фантастика
- Боевое фэнтези
- Эпическая фантастика
- Зарубежная фантастика
- Книги магов
- Мистика
- Разная фантастика
- Разное фэнтези
- Любовное фэнтези
- Романтическая фантастика
- Городское фентези
- Историческая фантастика
- Историческое фэнтези
- Социально-философская фантастика
- Сказочная фантастика
- Иностранное фэнтези
- Романтическое фэнтези
- Ироническая фантастика
- Ужасы и Мистика
- Постапокалипсис
- Героическая фантастика
- Фэнтези
- Научная Фантастика
- Социально-психологическая
- Альтернативная история
- Боевая фантастика
- Разная литература
- Пословицы, поговорки
- Шахматы
- Визуальные искусства
- Фанфик
- Авто и ПДД
- Военное
- Отраслевые издания
- Гиды, путеводители
- Зарубежная образовательная литература
- Военная история
- Цитаты из афоризмов
- Современная литература
- Великолепные истории
- Начинающие авторы
- Современная зарубежная литература
- Боевые искусства
- Изобразительное искусство, фотография
- Кино
- Литература 19 века
- Недвижимость
- Музыка, музыканты
- Готические новеллы
- Истории из жизни
- Культура и искусство
- Спецслужбы
- Зарубежная прикладная литература
- Музыка, танцы
- Подростковая литература
- Прочее
- Военная техника, оружие
- Газеты и журналы
- Периодические издания
- Домоводство, Дом и семья
- Любовные романы
- Экономическая литература
- Научные и научно-популярные книги
- О животных
- Биохимия
- Культурология
- Юриспруденция
- Политика
- Психотерапия
- Языкознание
- Научпоп
- Медицина
- Психология, личное
- Радиотехника
- Филология
- Педагогика
- Образовательная литература
- Деловая литература
- Физика
- Науки: разное
- Химия
- Воспитание детей, педагогика
- Детская психология
- Зарубежная публицистика
- Биология
- Архитектура
- Зарубежная психология
- Науки о космосе
- Социология
- Математика
- География
- Беременность, ожидание детей
- Литературоведение
- Транспорт, военная техника
- Обществознание
- Зоология
- География
- Альтернативная медицина
- Иностранные языки
- Ветеринария
- Рефераты
- Астрология
- Биофизика
- Экология
- История Европы
- Учебники
- Шпаргалки
- Государство и право
- Ботаника
- Религиоведение
- История
- Техническая литература
- Прочая научная литература
- Психология
- Детективы и Триллеры
- Справочная литература
- Проза
- Новелла
- Юмор
- Проза
- Историческая проза
- Русская современная проза
- Советская классическая проза
- Повести
- Очерки
- Эссе
- Рассказы
- Зарубежная современная проза
- Сентиментальная проза
- Зарубежная классика
- Афоризмы
- Эпистолярная проза
- Феерия
- Семейный роман/Семейная сага
- Разное
- Антисоветская литература
- Магический реализм
- Русская классическая проза
- Современная проза
- О войне
- Контркультура
- Классическая проза
- Поэзия, Драматургия
- Детская литература
- Детская фантастика
- Детские остросюжетные
- Детские стихи
- Детские приключения
- Внеклассное чтение
- Учебная литература
- Зарубежные детские книги
- Бизнес
- Загадки
- Книги для подростков
- Школьные учебники
- Детский фольклор
- Книги для дошкольников
- Детские детективы
- Детская познавательная и развивающая литература
- Буквари
- Прочая детская литература
- Сказка
- Детская проза
- Детская образовательная литература
- Юмор
- Документальные книги
- Бизнес
- Внешнеэкономическая деятельность
- Тайм-менеджмент
- Кадровый менеджмент
- О бизнесе популярно
- Управление, подбор персонала
- Бизнес
- Менеджмент и кадры
- Продажи
- Экономика
- Ценные бумаги и инвестиции
- Малый бизнес
- Делопроизводство, офис
- Корпоративная культура, бизнес
- Банковское дело
- Финансы
- Зарубежная деловая литература
- Ораторское искусство / риторика
- Личные финансы
- Интернет-бизнес
- Поиск работы
- Государственное и муниципальное управление
- Бухучет и аудит
- Работа с клиентами
- Менеджмент
- Краткое содержание
- Личная эффективность
- Переговоры
- Приключения
- Старинная литература
- Религия и духовность
- Компьютеры и Интернет
- Блог
Читаем без скачивания Сущность технологии СОМ. Библиотека программиста - Дональд Бокс
Шрифт:
Интервал:
Закладка:
Когда ненулевой указатель интерфейса копируется из одной ячейки памяти в другую, должен вызываться AddRef для извещения объекта о дополнительной ссылке.
Перед тем как произойдет перезапись той ячейки памяти, где содержится ненулевой указатель интерфейса, необходимо вызвать Release, чтобы известить объект, что ссылка уничтожается.
Избыточное количество вызовов AddRef и Release можно сократить, если иметь дополнительную информацию о связях между двумя и более ячейками памяти.
Аксиома о дополнительной информации введена главным образом для того, чтобы ввести возможность преобразования запутанных ситуаций в разумные и осмысленные идиомы программирования (например, стеки временных вызовов и сгенерированное компилятором занесение переменной в регистр не нуждаются в подсчете ссылок). Можно провести месяцы в поиске особых связей между переменными, содержащими явные указатели на интерфейс в программе и оптимизировать избыточные вызовы AddRef и Release , но поступать так было бы неосмотрительно. Выгода от удаления этих избыточных вызовов явно незначительна, так как даже в худшем случае, когда объект вызывается с расстояния более 8500 миль со средней скоростью передачи 14.4 кбит/сек, эти избыточные вызовы никогда не уйдут из вызывающего потока и нечасто требуют множество инструкций для выполнения.
Если руководствоваться приведенными выше тремя простыми аксиомами о подсчете ссылок в интерфейсных указателях, то можно записать это в виде руководящих принципов программирования, чтобы установить, когда вызывать и когда не вызывать AddRef и Release . Вот несколько типичных ситуаций, требующих вызова метода AddRef:
А1. Когда ненулевой интерфейсный указатель записывается в локальную переменную.
А2. Когда вызываемый объект пишет ненулевой интерфейсный указатель в параметр [out] или [in, out] метода или функции.
A3. Когда вызываемый объект возвращает ненулевой интерфейсный указатель как физический результат (physical result) функции.
А4. Когда ненулевой интерфейсный указатель пишется в элемент данных объекта.
Некоторые типичные ситуации, требующие вызова метода Release :
R1. Перед перезаписью ненулевой локальной переменной или элемента данных.
R2. Перед тем как покинуть область действия ненулевой локальной переменной.
R3. Когда вызываемый объект перезаписывает параметр [in,out] метода или функции, начальное значение которых отлично от нуля. Заметим, что параметры [out] предполагаются нулевыми при вводе и никогда не могут освобождаться вызываемым объектом.
R4. Перед перезаписью ненулевого элемента данных объекта.
R5. Перед завершением работы деструктора объекта, имеющего в качестве элемента данных ненулевой интерфейсный указатель.
Типичная ситуация, к которой применимо правило о дополнительной информации, возникает при передаче указателей интерфейсов функциям как параметрам [in]:
S1. Когда при вызове функции или метода ненулевой интерфейсный указатель передается через [in] -параметр, вызов AddRef и Release, не требуются, так как время жизни временной переменной в стеке является строгим подмножеством времени жизни выражения, использованного для инициализации формального аргумента.
Эти десять руководящих принципов охватывают ситуации, снова и снова возникающие при программировании в СОМ, и было бы неплохо их запомнить.
Чтобы конкретизировать правила подсчета ссылок в СОМ, предположим, что имеется глобальная функция, которая возвращает объекту интерфейсный указатель:
void GetObject([out] IUnknown **ppUnk);
и что имеется другая глобальная функция, которая выполняет некую полезную работу над объектом:
void UseObject([in] IUnknown *pUnk);
Написанный ниже код использует эти процедуры, чтобы управлять некоторыми объектами и возвращать интерфейсный указатель вызывающему объекту. Руководящие принципы, применимые к каждому оператору, указаны в комментариях к нему:
void GetAndUse(/* [out] */ IUnknown ** ppUnkOut)
{ IUnknown *pUnk1 = 0, *pUnk2 = 0; *ppUnkOut =0;
// R3
// get pointers to one (or two) objects
// получаем указатели на один (или два) объекта
GetObject(&pUnk1);
//A2
GetObject(&pUnk2);
//A1
// set pUnk2 to point to first object
// устанавливаем pUnk2, чтобы указать на первый объект
if (pUnk2) pUnk2->Release():
//R1
if (pUnk2 = pUnk1) pUnk2->AddRef():
//A1
// pass pUnk2 to some other function
// передаем pUnk2 какой-нибудь другой функции
UseObject(pUnk2);
//S1
// return pUnk2 to caller using ppUnkOut parameter
// возвращаем pUnk2 вызывающему объекту, используя
// параметр ppUnkOut
if (*ppUnkOut = pUnk2) (*ppUnkOut)->AddRef();
// A2
// falling out of scope so clean up
// выходит за область действия и поэтому освобождаем
if (pUnk1) pUnkl->Release();
//R2
if (pUnk2) pUnk2->Release();
//R2
}
Важно отметить, что в вышеприведенном коде правило A2 применяется дважды, но по двум разным причинам. При вызове GetObject код выступает как вызывающий объект, а реализация GetObject является вызываемым объектом. Это означает, что реализация GetObject является ответственной за вызов AddRef через параметр [out]. При перезаписи памяти, на которую ссылается ppUnkOut, код выступает как вызываемый объект и корректно вызывает AddRef через интерфейсный указатель перед возвратом управления вызывающему объекту.
Существуют некоторые тонкости относительно AddRef и Release, подлежащие обсуждению. Как AddRef, так и Release предназначались для возврата 32-битного целого числа без знака. Это целое число отражает общее количество оставшихся ссылок после применения операций AddRef или Release. Однако по целому ряду причин, связанных с многопоточным режимом, удаленным доступом и мультипроцессорной архитектурой, нельзя быть уверенным в том, что эта величина будет точно отражать общее число неосвобожденных интерфейсных указателей, и клиенту следует игнорировать ее, если только она не используется в целях диагностики при отладке.
Единственный случай, заслуживающий внимания, это когда Release возвращает нуль. Нулевой результат от Release надежно свидетельствует о том, что данный объект более не действителен ни в каком смысле. Однако обратное неверно. Это значит, что когда Release возвращает не нуль, нельзя утверждать, что объект еще работоспособен. Фактически, если Release был вызван указателем интерфейса столько же раз, сколько этим же указателем интерфейса был вызван AddRef , то данный указатель интерфейса недействителен и более не обеспечивает указание на действующий объект. В то же время возможно, что это – случайность, а объект все еще работоспособен благодаря другим, еще не освобожденным, указателям, и все может измениться в самый неподходящий момент. Чтобы однажды освобожденные (released) интерфейсные указатели более не использовались, можно, например, обнулять их сразу же после вызова метода Release:
inline void SafeRelease(IUnknown * &rpUnk)
{
if (rpUnk)
{
rpUnk->Release();
rpUnk = 0;
// rpUnk passed by reference
// rpUnk, переданный ссылкой
}
}
Когда этот способ применен, любое использование указателя интерфейса после его высвобождения немедленно вызовет ошибку доступа. Эта ошибка затем может быть достоверно воспроизведена и, можно надеяться, отловлена еще на этапе разработки.
Еще одна тонкость, относящаяся к AddRef и Release, состоит в выходе из блока. Функция GetAndUse , приведенная ранее, имеет только одну точку выхода. Это означает, что операторы, высвобождающие указатели интерфейса в конце функции, будут всегда выполняться ранее завершения работы функции. Если же функция завершит работу, не доходя до этих операторов – либо благодаря явному оператору return или же, что хуже, необработанному (unhandled) исключению C++, – то эти завершающие операторы будут пропущены и все ресурсы, удерживаемые неосвобожденными интерфейсными указателями, будут утеряны до окончания клиентской программы. Это означает, что к указателям интерфейса СОМ следует относиться с осторожностью, особенно при использовании их в средах, использующих исключения C++. Впрочем, это касается и других системных ресурсов, с которыми приходится работать, будь то семафоры или динамически распределяемая память. Далее в этой главе обсуждаются интеллектуальные СОМ-указатели, которые обеспечивают вызов Release во всех ситуациях.
Приведение типов и IUnknown
В предыдущей главе обсуждалось, почему необходимо определять тип на этапе выполнения в динамически собранной системе. Язык C++ предусматривает разумный механизм для динамического определения типа с помощью оператора dynamic_cast. Хотя эта языковая возможность имеет собственную реализацию для каждого компилятора, в предыдущей главе было предложено средство урегулирования этого – добавление к каждому интерфейсу явного метода, являющегося семантическим эквивалентом dynamic_cast. Ниже приводится IDL-описание QueryInterface: