类工厂

更新时间:2024-03-30 13:31

class factory(类工厂) 一个实现了IClassFactory接口的类,这允许它创建特定的对象,也被称为COM Class Object。类工厂的作用是在COM中,客户程序必须通过类工厂(Class Factory)来完成创建COM对象的任务。

基本功能

类工厂的实质是一个COM对象,它定义了一个IClassFactory接口,正是这个接口中的CreateInstance成员函数,对实例化COM组件起到了核心作用。

另外,根据COM规范,COM Class和类工厂是配对出现的。也就是说,只要有一个实现某一个或某几个接口的类被编写出来,若客户程序想对其进行实例化,就必须相应地实现与这个COM Class配对的类工厂。更具体地说,就是要实现IClassFactory接口。

举个实际的例子,如果想要使用已经编写的一些类作一些事情,就必须实现类工厂。反之,客户程序将无法访问到类的接口,那就不可能调用其成员函数。

在COM中,客户程序必须通过类工厂(Class Factory)来完成创建COM对象的任务。

注意:

类工厂也被称为COM Class Object,可能这个叫法对理解类工厂的用途更有帮助。

为了透彻了解类工厂的运作机制,让我们看一看类工厂的定义:

class IClassFactory : public IUnknown

{

virtual HRESULT CreateInstance(LPUNKNOWN pUnk ,

REFIID riid, void ** ppv) = 0

virtual HRESULT LockServer(BOOL fLock) = 0;

}

实现原理

为COM对象实现类工厂,首先,进行类工厂的声明,代码如下:

class CSimpleMath_ClassFactory : public IClassFactory

// 声明用于实现IClassFactory接口的对象

{

protected:

// 用于引用计数成员变量

long m_RefCount;

public:

CSimpleMath_MathClassFactory();

~ CSimpleMath_MathClassFactory();

// 由于IClassFactory接口继承自IUnknown接口

// 所以,也继承了IUnknown接口的三个方法

HRESULT QueryInterface(REFIID, void** );

ULONG AddRef();

ULONG Release();

// IClassFactory接口本身需要实现的方法

HRESULT CreateInstance(LPUNKNOWN, REFIID, void**);

HRESULT LockServer(BOOL bLock);

};

然后,具体实现的代码如下:

CSimpleMath_ClassFactory:: CSimpleMath_ClassFactory ()

{

// 对象构造时初始化引用计数为0

m_lRef = 0;

}

CSimpleMath_ClassFactory::~ CSimpleMath_ClassFactory ()

{

}

HRESULT CSimpleMath_ClassFactory::QueryInterface( REFIID riid, void** ppv )

{

*ppv = 0;

// 如果查询的接口为IUnknown或类工厂接口,则返回此实例

if ( riid == IID_IUnknown || riid == IID_ISimpleMathClassFactory )

*ppv = this;

if ( *ppv )

{

// 如果成功地引用到接口,则增加接口的引用计数

AddRef();

return S_OK;

}

return(E_NOINTERFACE);

}

ULONG CSimpleMath_ClassFactory::AddRef()

{

m_RefCount ++;

return m_RefCount;

}

ULONG CSimpleMath_ClassFactory::Release()

{

m_RefCount --; // 减小引用计数

if (m_RefCount == 0 )

{

// 如果引用计数减少至0,则释放之

delete this;

return 0;

}

return m_RefCount;

}

// *****************************//

// 用来产生COM对象的重要方法

// *****************************//

HRESULT CSimpleMath_ClassFactory::CreateInstance

( LPUNKNOWN pUnkOuter, REFIID riid, void** ppvObj )

{

CSimple_Math * p; // 声明指向COM Class的指针

HRESULT hr;

*ppvObj = 0;

// 创建COM Class的实例

p = new CSimple_Math;

// 如果没有成功地创建对象,则返回错误值

if (!p)

return(E_OUTOFMEMORY)

// 如果得到了对象,则可以调用QueryInterface方法

hr = p->QueryInterface( riid, ppvObj );

// 如果调用失败则做好善后工作,并将最终的状态返回

if ( FAILED( hr ) )

delete p;

return hr;

}

HRESULT CSimpleMath_ClassFactory::LockServer( BOOL bLock )

{

if ( bLock )

{

// 注意:g_LockedCount是COM对象中声明的一个全局计数变量

// 为了保障线程安全,需要对全局变量进行保护

InterlockedIncrement(g_LockedCount) ;

}

else

{

// 注意:g_LockedCount是COM对象中声明的一个全局计数变量

// 为了保障线程安全,需要对全局变量进行保护

InterlockedDecrement(g_LockedCount);

}

return S_OK;

}

这样就完成了类工厂的具体实现。在整个实现代码中,最关键的部分使用粗体字进行了标注。经过这样一番精心实现之后,客户程序便可以通过实例化类工厂COM对象,进而得到类工厂的IClassFactory接口指针,然后,通过对该接口中核心方法CreateInstance的调用完成COM对象的实例化。剩下的事情不言自喻—— 尽情调用COM对象中的服务吧。

主要事例

在开始介绍类工厂之前,先提一个小问题:

下面代码产生错误的原因是什么?

class CFoolish

{

public:

CFoolish();

~CFoolish();

int m_count;

}

int main()

{

CFoolish::m_count = 100;

return 0;

}

答案实在太明显了—— 没有创建对象就要访问类对象的非静态成员,才出错。创建完一个接口,并按照标准COM的要求实现了它,使客户程序可以通过QueryInterface方法来得到对象中的接口。但是,有一个问题尚未解决,那就是,我们没有看到一行可以令COM对象在内存中实例化的代码,因此,如果此时使用这个接口,就会犯与前面所提的简单问题中同种类型的错误。并且,需要提醒大家的是,由于COM对象的复杂性(与前面所讲的引用计数一样),COM实例的创建仍然要依赖内部机制来管理,而不能像普通语言对象一样,由客户程序使用简单的new、delete来控制。

免责声明
隐私政策
用户协议
目录 22
0{{catalogNumber[index]}}. {{item.title}}
{{item.title}}