本系列的第 1 模塊演示瞭如何創建窗口並響應窗口消息,如WM_PAINT和WM_CLOSE。 模塊 2 引入瞭組件對象模型 (COM) 。
COM 是創建可重用軟件組件的規范。 在基於新式Windows的程序中使用的許多功能都依賴於 COM,例如:
- 圖形 (Direct2D)
- 文本 (DirectWrite)
- Windows Shell
- 功能區控件
- UI 動畫
(此列表中的某些技術使用 COM 的子集,因此不是“純”COM.)
COM 有難以學習的聲譽。 的確,編寫支持 COM 的新軟件模塊可能很棘手。 但是,如果你的程序嚴格是 COM 的 使用者 ,你可能會發現 COM 比預期更容易理解。
本模塊演示如何在程序中調用基於 COM 的 API。 它還描述瞭 COM 設計背後的一些原因。 如果瞭解 COM 的設計原因,則可以更有效地對其進行編程。 本模塊的第二部分介紹瞭 COM 的一些推薦編程做法。
COM 於 1993 年引入,以支持對象鏈接和嵌入 (OLE) 2.0。 人們有時認為 COM 和 OLE 是一樣的。 這可能是 COM 難以學習的另一個原因。 OLE 2.0 是基於 COM 構建的,但你不必知道 OLE 才能理解 COM。
COM 是 二進制標準,而不是語言標準:它定義應用程序和軟件組件之間的二進制接口。 作為二進制標準,COM 是非語言中立的,盡管它自然映射到某些 C++ 構造。 本模塊將重點介紹 COM 的三個主要目標:
- 將對象的實現與其接口分離。
- 管理對象的生存期。
- 在運行時發現對象的功能。
一、什麼是 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.
};
和ShapeBitmap
類定義兩種不同的可繪制對象類型。 每個類繼承自 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 在接口和實現之間保持嚴格分離。 和類的ShapeBitmap
實現詳細信息可以更改(例如,修復 bug 或添加新功能),對調用代碼沒有更改。
在 C++ 實現中,使用類或結構聲明接口。
備註本主題中的代碼示例旨在傳達一般概念,而不是實際做法。 定義新的 COM 接口超出瞭此系列的范圍,但不會直接在頭文件中定義接口。 而是使用名為“接口定義語言” (IDL) 的語言定義 COM 接口。 IDL 文件由 IDL 編譯器處理,該編譯器生成 C++ 頭文件。
class IDrawable
{
public:
virtual void Draw() = 0;
};