Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Роджерсон Д. - Основы COM - 2000.pdf
Скачиваний:
412
Добавлен:
13.08.2013
Размер:
2.4 Mб
Скачать

4 глава

Подсчет ссылок

В детстве я хотел стать пожарным. Романтика приключений и опасности привлекала меня, как и большинство мальчиков. Однако по-настоящему мне хотелось быть пожарным не потому. Дело в том, что, во-первых, пожарные ездили, повиснув сзади на пожарной машине (из-за этого я еще хотел стать мусорщиком, но это другая история). Во-вторых, у пожарных была по-настоящему крутая экипировка: металлические каски, высокие ботинки, большие плащи и кислородные баллоны. Я тоже хотел носить все эти замечательные вещи. Пожарные, казалось, никогда не расставались с ними. Даже если они всего лишь снимали кошку с дерева, то все равно делали это в касках, плащах и высоких ботинках. Пожарного было видно издалека.

Класс С++ кое в чем напоминает пожарного. Заголовок сообщает всему миру, какие сервисы и функции предоставляет класс, — точно так же, как амуниция пожарных говорит об их профессии. Однако компоненты СОМ ведут себя совершенно иначе. Компонент СОМ гораздо более скрытен, чем пожарный или класс С++. Клиент не может посмотреть на компонент и сразу увидеть, что тот реализует пожарного. Вместо этого он должен выспрашивать: «Есть ли у Вас кислородный баллон? А топор? Носите ли Вы водонепроницаемую одежду?»

На самом деле клиенту неважно, имеет ли он дело с настоящим компонентом-пожарным. Ему важно, что у компонента за «амуниция». Например, если клиент задает вопрос «Носите ли Вы водонепроницаемую одежду?», то его удовлетворят ответы не только пожарного, но и байдарочника, аквалангиста и лесоруба в непромокаемом плаще. На вопрос «Есть ли у Вас кислородный баллон?» утвердительно ответит пожарный и аквалангист. На следующий вопрос про топор положительный ответ даст уже только пожарный (или аквалангист-лесоруб, если такой найдется).

Изоляция клиента от подлинной сущности компонента делает его менее восприимчивым к изменениям последнего. Однако, поскольку клиент знает компонент только через интерфейсы, он не может прямо управлять временем жизни компонента как такового. В этой главе мы рассмотрим косвенное управление — реализованное как явное управление временем жизни отдельных интерфейсов.

Управление временем жизни

Давайте разберем, почему клиент не должен управлять временем жизни компонента напрямую. Предположим, что наш клиент обращается к тому же компоненту-пожарному. В разных местах кода клиента могут быть разбросаны вызовы этого компонента через различные интерфейсы. Одна часть клиента может дышать через кислородный баллон при посредстве интерфейса IUseOxygen, а другая — крушить дом топором при помощи IUseAxe. Клиент может закончить пользоваться IUseAxe раньше, чем IUseOxygen. Однако Вы вряд ли захотите удалить компонент из памяти, когда с одним интерфейсом уже закончена, а с другим еще продолжается. Определить момент, когда компонент можно безопасно удалить, сложно еще и потому, что мы не знаем, указывают ли два указателя на интерфейсы одного и того же компонента. Единственный способ узнать это — запросить IUnknown через оба интерфейса и сравнить результаты. По мере того, как программа усложняется, становится все труднее определить, когда можно «отпускать» компонент. Проще всего загрузить его и не выгружать до завершения приложения. Но такое решение не слишком эффективно.

Итак, наша стратегия будет такова: вместо того, чтобы удалять компоненты напрямую, мы будем сообщать компоненту, что нам нужен интерфейс или что мы закончили с ним работать. Мы точно знаем, когда начинаем использовать интерфейс, и знаем (обычно), когда перестаем его использовать. Однако, как уже ясно, мы можем не знать, что закончили использовать компонент вообще. Поэтому имеет смысл ограничиться сообщением об окончании работы с данным интерфейсом — и пусть компонент сам отслеживает, когда мы перестаем пользоваться всеми интерфейсами.

Именно для реализации этой стратегии и предназначены еще две функции-члена IUnknown AddRef и Release. В прошлой главе было дано определение интерфейса IUnknown, которое я любезно повторю:

interface IUnknown

{

virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv) = 0;

Соседние файлы в предмете Программирование на C++