深入解析ATL第六章笔记

1:多重继承之传递夹层

由于C++的语法不能在派生类中区分来自不同基类,但原型一致的方法,所以类似的代码是不合法的

class CAcePowerll:public ICowBoy,
	public IArtist
{
	STDMETHODIMP ICowBoy::Draw();
	STDMETHODIMP IAartist::Draw();
};

若要在com类中实现来自不同接口但原型一致的方法,则可借助传递夹层

struct _IArtist:public IArtist
{
	STDMETHODIMP Draw(){return ArtistDraw();}
	STDMETHODIMP ArtistDraw() = 0;
};
struct _ICowBoy:public ICowBoy
{
	STDMETHODIMP Draw(){return ICowBoyDraw();}
	STDMETHODIMP ICowBoyDraw() = 0;
};
class CAcePowerll:public _IArtist,
	public _ICowBoy
{
	STDMETHODIMP ArtistDraw();
	STDMETHODIMP ICowBoyDraw();
};

_IArtist和_ICowboy都是过渡类,他们分别实现2个名字冲突的方法,并把方法传递给另一名字唯一的纯虚函数,这个技巧用_IArtist::Draw和_ICowboy::Draw填充IArtist和ICowboy的vtbl,传递夹层消除了名字冲突,代价是每个过渡类需要一个额外的虚表,额外的虚函数调用。还可以用标准的ATL技巧来实现

template<typename Deriving>
struct ATL_NO_VTABLE _IArtist:public IArtist
{
	STDMETHODIMP Draw()
	{
		return static_cast<Deriving*>(this)->ArtistDraw();
	}
};

template<typename Deriving>
struct ATL_NO_VTABLE _ICowBoy:public ICowBoy
{
	STDMETHODIMP Draw()
	{
		return static_cast<Deriving*>(this)->ICowBoyDraw();
	}
};

class ATL_NO_VTABLE CAcePowerll:public _IArtist<CAcePowerll>
			     public _ICowBoy<CAcePowerll>
{
	STDMETHODIMP ArtistDraw();
	STDMETHODIMP ICowBoyDraw();
};

这种方法在有个缺点,在com类中不能提供Draw的实现,否则会屏蔽_IArtist::Draw/_ICowBoy::Draw的调用

 

2:接口着色

如果2个类的虚表有相同的方法数目,并且相同偏移量上的方法有相同的原型特征,则可使用接口着色技术:两个布局兼容的类可以类型不兼容。其实很简单,只不过是按照COM所要求的虚表结构另行构造而已,其好处是可以实现两个接口语义完全相同但是IID却不相同的接口。这也体现了COM接口实现的灵活性

class IA
{
public:
	virtual void PrintA() = 0;
};

class IB
{
public:
	virtual void PrintB() = 0;
};

class CB:public IB
{
public:
	void PrintB()
	{
		cout<<"B"<<endl;
	}
};

int main()  
{  
	IB* pB = new CB;
	IA* pA = reinterpret_cast<IA*>(pB);
	pA->PrintA();
	return 0;  
}  


3.Tear-Off

Teaf-Off的主要目的是把一些很少需要访问的接口抽离出来单独生成一个内部com类,等需要访问接口时才在QueryInterface中去创建此com类并访问相应的接口,从而达到减少外部com类的大小(继承实现的每个接口都有虚指针和虚表)。实现tear-off接口时使用CComTearoffObjectBase作为基类并缓存所有者的IUnknow指针。外部组建通过COM_INTERFACE_ENTRY_TEAR_OFF设置创建内部组建的函数指针

class CA;
class ATL_NO_VTABLE CB : 
	public CComTearOffObjectBase<CA,CComSingleThreadModel>,
	public CComCoClass<CB, &CLSID_B>,
	public IB
{

}

class ATL_NO_VTABLE CA : 
	public CComObjectRootEx<CComSingleThreadModel>,
	public CComCoClass<CA, &CLSID_A>,
	public IA
{
public:
	CA(){}

	BEGIN_COM_MAP(CA)
		COM_INTERFACE_ENTRY(IA)
		COM_INTERFACE_ENTRY_TEAR_OFF(IID_IB,CB)
		COM_INTERFACE_ENTRY_CACHED_TEAR_OFF(IID_IC,CC,m_spunk.p)
	END_COM_MAP()
}
//COM_INTERFACE_ENTRY_TEAR_OFF保存了创建CB的函数指针,当用户通过组建CA的接口查询IB时便会调用_Creator来创建CB
#define COM_INTERFACE_ENTRY_TEAR_OFF(iid, x)\
{&iid,\
	(DWORD_PTR)&ATL::_CComCreatorData<\
	ATL::CComInternalCreator< ATL::CComTearOffObject< x > >\
	>::data,\
	_Creator},

static HRESULT WINAPI _Creator(void* pv, REFIID iid, void** ppvObject, DWORD_PTR dw)
{
	_ATL_CREATORDATA* pcd = (_ATL_CREATORDATA*)dw;//将"偏移量"转换成函数指针
	return pcd->pFunc(pv, iid, ppvObject);
}

template <class Base>
class CComTearOffObject : public Base
{
public:
	CComTearOffObject(void* pv)
	{
		ATLASSUME(m_pOwner == NULL);
		m_pOwner = reinterpret_cast<Base::_OwnerClass*>(pv);//Base::_OwnerClass就是CB派生的模版类CComTearOffObjectBase中的参数"CA"
		m_pOwner->AddRef();
	}
	// Set refcount to -(LONG_MAX/2) to protect destruction and 
	// also catch mismatched Release in debug builds
	virtual ~CComTearOffObject()
	{
		m_dwRef = -(LONG_MAX/2);
		FinalRelease();
#ifdef _ATL_DEBUG_INTERFACES
		_AtlDebugInterfacesModule.DeleteNonAddRefThunk(_GetRawUnknown());
#endif
		m_pOwner->Release();
	}

	STDMETHOD_(ULONG, AddRef)() throw() {return InternalAddRef();}
	STDMETHOD_(ULONG, Release)() throw()
	{
		ULONG l = InternalRelease();
		if (l == 0)
			delete this;
		return l;
	}
	STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject) throw()
	{
		return m_pOwner->QueryInterface(iid, ppvObject);//将查询都转发给外部组建(CA)
	}
};
tear-off与聚合很相似

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值