什么是 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 在接口和实现之间保持严格的分离。 和 Bitmap
类的Shape
实现详细信息可以更改(例如,修复 bug 或添加新功能),无需更改调用代码。
在 C++ 实现中,使用类或结构声明接口。
注意
本主题中的代码示例旨在传达一般概念,而不是实际做法。 定义新的 COM 接口超出了本系列的范围,但你不会直接在头文件中定义接口。 而是使用名为接口定义语言 (IDL) 的语言定义 COM 接口。 IDL 文件由 IDL 编译器处理,该编译器生成 C++ 头文件。
class IDrawable
{
public:
virtual void Draw() = 0;
};
使用 COM 时,请务必记住接口不是对象。 它们是对象必须实现的方法集合。 多个对象可以实现相同的接口,如 和 Bitmap
示例所示Shape
。 此外,一个对象可以实现多个接口。 例如,图形库可以定义一个名为 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 库。