Клиент этого «интеллектуального OLE» вызвал бы CoCreateInstance или ClassFactory::CreateInstance, чтобы получить указатель на объект (а не на интерфейс!). Используя этот указатель, клиент вызвал бы QueryInterface, чтобы получить интерфейс. Если бы клиент хотел получить другой интерфейс, он или она сделали бы другое обращение QueryInterface через объект, а не через интерфейс. Вы не могли бы получать интерфейс из другого интерфейса. Только объект обладал бы способностью распределять интерфейсы. Bye, bye IUnknown!
Позвольте мне показать Вам некоторый гипотетический код в этом новом «интеллектуальное OLE».
CoObject* obj CoCreateInstance(CLSID_FooBarObject);
IFoo* foo = obj->QueryInterface(IID_FOO);
foo->FooMethod();
IBar* bar = obj->QueryInterface(IID_BAR);
bar->BarMethod();
delete obj;
Я преднамеренно опустил всю проверку ошибок и подсчет ссылок. Фактически, я не написал бы код, подобный этому, в серьезном приложении, я использовал бы интеллектуальные указатели и исключения. Но обратете внимание на одну вещь, в «интеллектуальном OLE» наследование столь же просто как и в C++. Здесь не имеется никакого способа перейти от интерфейса к интерфейсу и нет никакого IUnknown. Расширение FooObject, добавлением IBar требует не больше работы чем создание FooBarObject, наследующего от FooObject и IBar, реализующего методы IBar и переписывание метода QueryInterface CoObject. Я полагаю, что все «интеллектуальное OLE» объекты наследуют от абстрактного класса CoObject и переопределяют его метод QueryInterface (это очень отличается от наличия всех интерфейсов, наследующих от IUnknown!).
Что сказать о подсчете ссылок? Вполне очевидно, что имеются очень немного потребностей в подсчете ссылок (refcounting), пока Вы соглашаетесь не уничтожать объект, в то время пока Вы используете его интерфейсы. Это не такое большое дело. Мы делаем это все время, когда используем методы в C++. Мы не думаем о том, что это особенно жесткое требование: не уничтожить объект, в то время как мы используем его методы. Если мы должны следовать за текущей моделью OLE везде, мы должны требовать, чтобы клиент получал счетчик ссылок любого метода, который он, или она планируют использовать, а затем освобождать его после вызова? Это было бы абсурдно, не так ли?
Так, почему же OLE так придирчиво считает ссылки? Простое объяснение: потому что это скрывает объект от клиента. OLE-объект создается неявно, когда Вы получаете его первый интерфейс, и разрушается неявно, когда Вы освобождаете его последний интерфейс. Вы видите, что OLE делает для Вас большую пользу, скрывая эту бухалтерию от Вас. Так ли это? Забавный вопрос.