Breakpoint1
interface IX : IUnknown 这里使用struct的默认继承,但是在VS2010中的Object Browser窗口中看到的是接口的private继承这个似乎和struct的作用相违背啊(~挺疑惑的~)。
Breakpoint2 之后的几句代码中实现接口中继承到的方法,你会发现IUnknown继承的方法并没有使用类名来区分而display则分别使用IX::和IY::来区分。
前者说明从IX和IY继承到的IUnknown方法的实现方式都是一样的(p.33)而后者则需要使用不同的是方式来表达自己。
Breakpoint3之后的代码为 QueryInterface的具体实现,在原书中使用的是纯if...else...方式,如果细心阅读会发现这段代码很长重复了很多。如果我们将if...else...改成if语句实现就很容易发现这点。再次观察修改后的代码就很容易想到是不是可以用#define来简化呢?
答案是肯定的,如下提供代码供参考:
在server.h代码中加入
// macros for QueryInterface implement
# define QUERY_IMPL_BEGIN() *ppv = NULL
# define IDENTIFY3(tmp1,tmp2,tmp3) \
if(iid == IID_##tmp1##) \
{\
trace("QueryInterface: Return pointer to "#tmp1"."); \
*ppv = static_cast<##tmp1##*>(static_cast<##tmp2##*>(static_cast<##tmp3##>(this))); \
}\
# define IDENTIFY2(tmp1, tmp2) \
if(iid == IID_##tmp1##) \
{\
trace("QueryInterface: Return pointer to "#tmp1"."); \
*ppv = static_cast<##tmp1##*>(static_cast<##tmp2##*>(this)); \
}\
# define IDENTIFY1(tmp1) \
if(iid == IID_##tmp1##) \
{\
trace("QueryInterface: Return pointer to "#tmp1" ."); \
*ppv = static_cast<##tmp1##*>(this); \
}\
# define IDENTIFY0() \
if(NULL == *ppv) \
{\
trace("QueryInterface: Interface not found."); \
return E_NOINTERFACE; \
}\
# define QUERY_IMPL_END() \
reinterpret_cast<IUnknown*>(*ppv)->AddRef(); \
return S_OK; \
// end defining QueryInterface implement
QueryInterface可以简化为:
HRESULT CX::QueryInterface(const IID& iid , void ** ppv)
{
QUERY_IMPL_BEGIN();
IDENTIFY2(IUnknown, IX);
IDENTIFY1(IX);
IDENTIFY1(IY);
IDENTIFY0();
QUERY_IMPL_END();
}
Breakpoint4
原书使用CreateInstance()函数来实现CA类的创建,我觉得这里存在漏洞。首先考虑CreateInstance 这个例程应该放在服务端还是客户端呢?如果在服务端就必须提供多个这样的函数来实现多个类的创建而且还必须暴露这个函数很明显不方便,那就放在客户端了。为了简化我这里使用auto变量来实现。
通过上面简单的实现可以简单的看出COM的工作。
留下几个问题:
可以使用dynamic_cast实现转换么?
为啥非得使用void**,void*不可以么?
如何对CreateInstance进行合理的内存管理?