Что такое COM-интерфейс?
Если вы знаете C# или Java, интерфейсы должны быть знакомой концепцией. Интерфейс определяет набор методов, которые может поддерживать объект, не диктуя ничего о реализации. Интерфейс отмечает четкую границу между кодом, который вызывает метод, и кодом, реализующим метод . С точки зрения информатики вызывающий объект отделяется от реализации.
В C++ ближайшим эквивалентом интерфейса является чистый виртуальный класс, то есть класс, который содержит только чистые виртуальные методы и никакие другие члены. Ниже приведен гипотетический пример интерфейса:
// The following is not actual COM.
// Pseudo-C++:
interface IDrawable
{
void Draw();
};
Идея этого примера заключается в том, что набор объектов в некоторых графических библиотеках можно прорисовать. Интерфейс IDrawable
определяет операции, которые должен поддерживать любой рисуемый объект. (По соглашению имена интерфейсов начинаются с "I".) В этом примере IDrawable
интерфейс определяет одну операцию: Draw
.
Все интерфейсы являются абстрактными, поэтому программе не удалось создать экземпляр объекта как такового IDrawable
. Например, следующий код не будет компилироваться.
IDrawable draw;
draw.Draw();
Вместо этого библиотека графики предоставляет объекты, реализующиеIDrawable
интерфейс . Например, библиотека может предоставлять объект фигуры для рисования фигур и растровый объект для рисования изображений. В C++ это делается путем наследования от общего абстрактного базового класса:
class Shape : public IDrawable
{
public:
virtual void Draw(); // Override Draw and provide implementation.
};
class Bitmap : public IDrawable
{
public:
virtual void Draw(); // Override Draw and provide implementation.
};
Классы Shape
и Bitmap
определяют два разных типа рисуемых объектов. Каждый класс наследует от IDrawable
и предоставляет собственную реализацию Draw
метода . Естественно, эти две реализации могут значительно отличаться. Например, Shape::Draw
метод может растеризовать набор строк, а Bitmap::Draw
массив пикселей будет вырезать.
Программа, использующий эту графическую библиотеку, будет управлять объектами Shape
и Bitmap
с помощью IDrawable
указателей, а не напрямую использовать Shape
указатели или Bitmap
.
IDrawable *pDrawable = CreateTriangleShape();
if (pDrawable)
{
pDrawable->Draw();
}
Ниже приведен пример циклического перебора массива указателей IDrawable
. Массив может содержать разнородный набор фигур, растровых изображений и других графических объектов, если каждый объект в массиве наследует IDrawable
.
void DrawSomeShapes(IDrawable **drawableArray, size_t count)
{
for (size_t i = 0; i < count; i++)
{
drawableArray[i]->Draw();
}
}
Ключевым моментом com является то, что вызывающий код никогда не видит тип производного класса. Другими словами, вы никогда не объявите переменную типа Shape
или Bitmap
в коде. Все операции с фигурами и растровыми рисунками выполняются с помощью IDrawable
указателей. Таким образом, COM поддерживает строгое разделение между интерфейсом и реализацией. Сведения о Shape
реализации классов и Bitmap
могут изменяться, например, для исправления ошибок или добавления новых возможностей без каких-либо изменений в вызывающем коде.
В реализации C++ интерфейсы объявляются с помощью класса или структуры.
Примечание
Примеры кода в этом разделе предназначены для передачи общих понятий, а не реальной практики. Определение новых COM-интерфейсов выходит за рамки область этой серии, но вы не будете определять интерфейс непосредственно в файле заголовка. Вместо этого COM-интерфейс определяется с помощью языка, называемого языком определения интерфейса (IDL). IDL-файл обрабатывается компилятором IDL, который создает файл заголовка C++.
class IDrawable
{
public:
virtual void Draw() = 0;
};
При работе с COM важно помнить, что интерфейсы не являются объектами. Они представляют собой коллекции методов, которые должны реализовывать объекты . Несколько объектов могут реализовать один и тот же интерфейс, как показано в Shape
примерах и Bitmap
. Кроме того, один объект может реализовывать несколько интерфейсов. Например, библиотека графики может определить интерфейс с именем ISerializable
, поддерживающий сохранение и загрузку графических объектов. Теперь рассмотрим следующие объявления классов:
// An interface for serialization.
class ISerializable
{
public:
virtual void Load(PCWSTR filename) = 0; // Load from file.
virtual void Save(PCWSTR filename) = 0; // Save to file.
};
// Declarations of drawable object types.
class Shape : public IDrawable
{
...
};
class Bitmap : public IDrawable, public ISerializable
{
...
};
В этом примере Bitmap
класс реализует ISerializable
. Программа может использовать этот метод для сохранения или загрузки растрового изображения. Однако Shape
класс не реализует ISerializable
, поэтому он не предоставляет эту функциональность. На следующей схеме показаны отношения наследования в этом примере.
В этом разделе рассматривается концептуальная основа интерфейсов, но до сих пор мы не видели фактический COM-код. Начнем с первого, что должно сделать любое com-приложение: инициализация библиотеки COM.