分类: C/C++
2008-03-18 16:44:33
不要过分抽象
ATL最不直观的一个方面是你所定义和实现的C++类仍然是抽象基类。没错,在ATL的模板类和宏上辛苦了半天,却仍然得不到一个可以实例化的类。即使你从 CComObjectRootEx 派生,其结果同从一个或更多的ATL接口实现继承一样。从技术上讲,你的对象不提供 IUnknown 三个核心方法(QueryInterface,AddRef 和 Release)的实现。如果你检查现有ATL之前的 COM 实现,如果不是全部,那么也是大多数的方法实现并不在乎这个类是不是被用于COM聚合或tear-off,是不是被用于独立的对象或一个包含在内的数据成员,是不是要作为基于堆的对象或作为全局变量,以及是不是对象存在时,一直要保持服务器运行。为了允许最大限度的灵活性,所有这些方面分别通过ATL家族中的十个类属的 CComObject 之一来说明。参见下表:
类名 | 服务器是否加锁 | 是否代理IUnknown | 是否删除对象 | 备注 |
CComObject | Yes | No | Yes | 常规情况 |
CComObjectCached | Yes(在第二次AddRef之后) | No | Yes | 用于通过内部指针控制的对象 |
CComObjectNoLock | No | No | Yes | 用于不控制服务器运行的对象 |
CComObjectGlobal | Yes(在第一次AddRef之后) | No | No | 用于全程变量 |
CComObjectStack | No | No | No | 用于不能被增加引用计数的基于堆栈的变量 |
CComContainedObject | No | Yes | No | 用于MFC风格的嵌套类 |
CComAggObject | Yes | Yes | Yes | 仅用于聚合实现 |
CComPolyObject | Yes | Yes(如果聚合) | Yes | 用于聚合/非聚合实现 |
CComTearOffObject | No | Yes(仅用于QueryInterface) | Yes | 用于每次请求所创建的tear-offs |
CComCachedTearOffObject | No | Yes(通过第二个IUnknown) | Yes | 用于在第一次请求和缓存时所创建的tear-offs |
template它假设你的对象将在堆中分配,也就是说最终调用 Release 时,将触发这个对象的 delete 操作。CComObjectNoLock 假设你的对象不是可聚合的,并且在服务器运行时,对象并不一直存在(因此有后缀 NoLock)。class CComObjectNoLock : public Base { public: typedef Base _BaseClass; CComObjectNoLock(void* = NULL){} ~CComObjectNoLock() {m_dwRef = 1L; FinalRelease();} STDMETHOD_(ULONG, AddRef)() {return InternalAddRef();} STDMETHOD_(ULONG, Release)() { ULONG l = InternalRelease(); if (l == 0) delete this; return l; } STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject) {return _InternalQueryInterface(iid, ppvObject);} }; template class CComObject : public Base { public: typedef Base _BaseClass; CComObject(void* = NULL) { _Module.Lock(); } ~CComObject() {m_dwRef = 1L; FinalRelease(); _Module.Unlock(); } STDMETHOD_(ULONG, AddRef)() {return InternalAddRef();} STDMETHOD_(ULONG, Release)() { ULONG l = InternalRelease(); if (l == 0) delete this; return l; } STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject) {return _InternalQueryInterface(iid, ppvObject);} static HRESULT WINAPI CreateInstance(CComObject** pp); };
IPager *p = new CComObjectNoLockCComObjectNoLock();