com学习新体会(2)

wishfly 2007-07-23 11:27:32
在ATL中,类厂是计数的。

1. CComCoClass
template <class T, const CLSID* pclsid = &CLSID_NULL>
class CComCoClass
{
public:
DECLARE_CLASSFACTORY()

...
}

2.
#define DECLARE_CLASSFACTORY() DECLARE_CLASSFACTORY_EX(ATL::CComClassFactory)

3. 类厂的_ClassFactoryCreatorClass
#if defined(_WINDLL) | defined(_USRDLL)
#define DECLARE_CLASSFACTORY_EX(cf) typedef ATL::CComCreator< ATL::CComObjectCached< cf > > _ClassFactoryCreatorClass;
#else
// don't let class factory refcount influence lock count
#define DECLARE_CLASSFACTORY_EX(cf) typedef ATL::CComCreator< ATL::CComObjectNoLock< cf > > _ClassFactoryCreatorClass;
#endif

4.1 对于in-pro
template <class Base>
class CComObjectCached : public Base
{
public:
typedef Base _BaseClass;
CComObjectCached(void* = NULL){}
// Set refcount to -(LONG_MAX/2) to protect destruction and
// also catch mismatched Release in debug builds
~CComObjectCached()
{
m_dwRef = -(LONG_MAX/2);
FinalRelease();
#ifdef _ATL_DEBUG_INTERFACES
_AtlDebugInterfacesModule.DeleteNonAddRefThunk(_GetRawUnknown());
#endif
}
//If InternalAddRef or InternalRelease is undefined then your class
//doesn't derive from CComObjectRoot
STDMETHOD_(ULONG, AddRef)()
{
m_csCached.Lock();
ULONG l = InternalAddRef();
if (m_dwRef == 2)
_pAtlModule->Lock();
m_csCached.Unlock();
return l;
}
STDMETHOD_(ULONG, Release)()
{
m_csCached.Lock();
InternalRelease();
ULONG l = m_dwRef;
m_csCached.Unlock();
if (l == 0)
delete this;
else if (l == 1)
_pAtlModule->Unlock();
return l;
}
//if _InternalQueryInterface is undefined then you forgot BEGIN_COM_MAP
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
{return _InternalQueryInterface(iid, ppvObject);}
CComGlobalsThreadModel::AutoCriticalSection m_csCached;
};

4.2 对于local server or service
template <class Base>
class CComObjectNoLock : public Base
{
public:
typedef Base _BaseClass;
CComObjectNoLock(void* = NULL){}
// Set refcount to -(LONG_MAX/2) to protect destruction and
// also catch mismatched Release in debug builds
~CComObjectNoLock()
{
m_dwRef = -(LONG_MAX/2);
FinalRelease();
#ifdef _ATL_DEBUG_INTERFACES
_AtlDebugInterfacesModule.DeleteNonAddRefThunk(_GetRawUnknown());
#endif
}

//If InternalAddRef or InternalRelease is undefined then your class
//doesn't derive from CComObjectRoot
STDMETHOD_(ULONG, AddRef)() {return InternalAddRef();}
STDMETHOD_(ULONG, Release)()
{
ULONG l = InternalRelease();
if (l == 0)
delete this;
return l;
}
//if _InternalQueryInterface is undefined then you forgot BEGIN_COM_MAP
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
{return _InternalQueryInterface(iid, ppvObject);}
};

由此可见,在ATL中类厂是计数的。
只不过对于in-pro, 只有当m_dwRef由1->2时,才lock server;当m_dwRef由2->1时,就unlock server;通过这种手段来避免“自相矛盾”。

而对于local server or service, 计数但不lock/unlock server.
...全文
775 46 打赏 收藏 转发到动态 举报
写回复
用AI写文章
46 条回复
切换为时间正序
请发表友善的回复…
发表回复
lbb2006 2007-08-10
  • 打赏
  • 举报
回复
wishfly以不能发帖,换个马甲:)
深入解析ATL -- ¥69 -- 好像现在没卖的了
wishfly 2007-08-09
  • 打赏
  • 举报
回复
这些天看COM/ATL的书,感觉到处都写着接口.
不管是COM异常处理,集合/枚举,还是连接点,处处都体现接口的光芒.

如果你觉得某些实现必须要传递某个结构或一个数组---且慢!一定是接口设计没有到位!
接口粒度还不够小!

1.传递结构数据的话,可以参考COM处理的实现
---用SetErrorInfo(,*IErrorInfo)和GetErrorInfo(,*IErrorInfo),来抛出或截获COM异常.
其实它也在作"传递有关异常的结构数据",但它采用的接口的方式.避免结构数据的传递.
最终结构数据的传递,是通过接口IErrorInfo中的一些get/set实现的.

所以说,组件编程,要换脑子,避免以前养成的编程定势,处处要以接口方式思考问题.
可以将组件想象成一个芯片----只想怎样通过管脚来实现功能,而不是奢望打开芯片取数据---那是不可能

的!!

总结:组件编程,是不同于以往OP,OO编程的新的编程方式.-----一切都是接口!
smalllixin 2007-08-09
  • 打赏
  • 举报
回复
采访一下,你看什么书学的,how much
wishfly 2007-08-08
  • 打赏
  • 举报
回复
现在讨论以下tear off技术
tear off只要用于当接口很多,容易造成接口"膨胀"的情况.
比如:
class CBeachBall :
public CComObjectRootEx<CBeachBall>,
public ISphere,
public IRollableObject,
public IPlaything,
public ILethalObject, <====不经常用
public ITakeUpSpace,
public IWishIWereMoreUseful,
public ITryToBeHelpful,
public IAmDepressed {...};

CBeachBall一共实现了8个接口,这样每个组件实例都要由8个vptr,需要额外32个字节的开销.
而这8个接口并不是每个都很常用.

比如ILethalObject就不经常,但还占用每个组件实例的4个字节的空间---浪费!!

怎么办?

使用tear off技术来解决!

不是ILethalObject不常用吗, 那就将其从接口列表中去掉!当真有ILethalObject接口请求时,动态生成
实现ILethalObject接口的实例,然后调用这个实例中的实现.

如下:
CBeachBallLethalness实现了ILethalObject接口

class CBeachBallLethalness :
public CComTearOffObjectBase<CBeachBall,
CComSingleThreadModel>,
public ILethalObject {
public:
BEGIN_COM_MAP(CBeachBallLethalness)
COM_INTERFACE_ENTRY(ILethalObject)
END_COM_MAP()

// ILethalObject methods
STDMETHODIMP Kill() {
m_pOwner->m_gasFill = GAS_HYDROGEN;
m_pOwner->HoldNearOpenFlame();
return S_OK;
}
};

去掉ILethalObject接口, CBeachBall变成:
class CBeachBall :
public CComObjectRootEx<CBeachBall>,
public ISphere,
public IRollableObject,
public IPlaything,
//public ILethalObject, // 去掉ILethalObject接口
public ITakeUpSpace,
public IWishIWereMoreUseful,
public ITryToBeHelpful,
public IAmDepressed {
public:
BEGIN_COM_MAP(CBeachBall)
COM_INTERFACE_ENTRY(ISphere)
COM_INTERFACE_ENTRY(IRollableObject)
COM_INTERFACE_ENTRY(IPlaything)
COM_INTERFACE_ENTRY_TEAR_OFF(IID_ILethalObject,
CBeachBallLethalness) // ILethalObject接口由CBeachBallLethalness实现
COM_INTERFACE_ENTRY(ITakeUpSpace)
COM_INTERFACE_ENTRY(IWishIWereMoreUseful)
COM_INTERFACE_ENTRY(ITryToBeHelpful)
COM_INTERFACE_ENTRY(IAmDepressed)
END_COM_MAP()
...
private:
GAS_TYPE m_gasFill;
void HoldNearOpenFlame();
// Tear-offs are generally friends
friend class CBeachBallLethalness; // CBeachBallLethalness作为友元加入
};


总结:tear off技术就是要去掉组件中--"不常用接口",减轻每个实例的内存开销.
只有当真正使用该接口时,在动态生成"实现该接口的组件",实现调用.
lbb2006 2007-08-08
  • 打赏
  • 举报
回复
mark
wishfly 2007-08-06
  • 打赏
  • 举报
回复
有了static_cast和dynamic_cast,就可以在类继承树上随意"游动",获得类型之间的转换.
wishfly 2007-08-06
  • 打赏
  • 举报
回复
比较static_cast和dynamic_cast
static_cast---靠程序员来检查类型转换的完整性
dynamic_cast---runtime检查类型转换的完整性
wishfly 2007-08-06
  • 打赏
  • 举报
回复
dynamic_cast主要是对指针或引用进行转换.
它检查被转换方的数据完整性.
因此dynamic_cast对从派生类向基类转换总是成功的.

比如:
class CBase { };
class CDerived: public CBase { };

CBase b; CBase* pb;
CDerived d; CDerived* pd;

pb = dynamic_cast<CBase*>(&d); // ok: derived-to-base
pd = dynamic_cast<CDerived*>(&b); // wrong: dynamic_cast不能将基类向派生类转换

但dynamic_cast可以对虚类作"基类向派生类转换".但必须runtime检查"被转换方的数据完整性".

例如:
class CBase { virtual void dummy() {} };
class CDerived: public CBase { int a; };

int main () {
try {
CBase * pba = new CDerived;
CBase * pbb = new CBase;
CDerived * pd;

pd = dynamic_cast<CDerived*>(pba);
if (pd==0) cout << "Null pointer on first type-cast" << endl;

pd = dynamic_cast<CDerived*>(pbb);
if (pd==0) cout << "Null pointer on second type-cast" << endl;

} catch (exception& e) {cout << "Exception: " << e.what();}
return 0;
}

其中:

CBase * pba = new CDerived;
CBase * pbb = new CBase;

虽然pba 和pbb都指向CBase *类型.
但pba 实际指向完整的object of CDerived.
而pbb指向object of CBase, 非完整的object of CDerived.

而dynamic_cast会runtime检查"被转换方的数据完整性".

pd = dynamic_cast<CDerived*>(pba); <===== 成功! pba指向完整的object of CDerived.

pd = dynamic_cast<CDerived*>(pbb); <===== 失败! pba指向非完整的object of CDerived.


总结:dynamic_cast就像它名字一样----要runtime检查"被转换方的数据完整性".
caitian6 2007-08-06
  • 打赏
  • 举报
回复
up
wishfly 2007-08-06
  • 打赏
  • 举报
回复
=======================
static_cast与reinterpret_cast
static_cast必须是对在具有继承关系的类型之间转换.
而对reinterpret_cast, 互相转换的类型之间可以没有继承关系.
就像它名字一样-----对转换后的内容"重新解释".
有点强制转换的意思!:)

对reinterpret_cast, 在任何情况下reinterpret_cast转换,指针的地址不变.

pIBase1 = reinterpret_cast<IBase1 *>pIBase2;

pIBase1与pIBase2地址一样,但经reinterpret_cast"强制"转换后,pIBase1调用的IBase1的方法.
----很危险!

那什么情况下会用到reinterpret_cast呢?

lbb2006 2007-08-06
  • 打赏
  • 举报
回复
mark
wishfly 2007-08-06
  • 打赏
  • 举报
回复
另外需要注意的是, type_cast对指针地址的影响

1.单继承时,不管怎么在类型之间type_cast转换,指针的地址不变.
2.而对多继承时,类型之间type_cast转换,指针的地址是可能变化的.
class Drive : public IBase1, public IBase2 {
}

Drive *pDrive = new Drive;

// "由派生类转换成基类"
IBase1 *pIBase1 = pDrive; // pIBase1 = pDrive
IBase1 *pIBase2 = pDrive; // pIBase2 != pDrive == 地址改变

//"由基类转换成派生类"的"逆"转换
pDrive = type_cast<Drive *>pIBase1; //pDrive = pIBase1
pDrive = type_cast<Drive *>pIBase2; //pDrive != pIBase2
wishfly 2007-08-06
  • 打赏
  • 举报
回复
继续讨论type_cast

type_cast主要作用是在继承树的各类之间作"逆"转换---"由基类转换成派生类".
而所谓的"正"转换---"由派生类转换成基类",是隐含转换,不需要type_cast.

1."由派生类转换成基类"---好理解, 运用虚函数实现多态时,经常用.

=================== 虚函数 =====================
#include <iostream>
using namespace std;

class Base {
public:
virtual void fun() {
cout << "Base::fun" << endl;
}
};

class Drive : public Base {
public:
void fun() {
cout << "Drive::fun" << endl;
}
};

int main() {
Base *pBase;
Drive *pDrive = new Drive;

//
pBase = pDrive; <====== "由派生类转换成基类" ---正转换
pBase->fun(); <====== 调用Drive::fun

return 0;
}

2.而"由基类转换成派生类"的"逆"转换什么时候用到呢?
-------那就是要实现模版形式的多态时!!

=================== 模版 =====================

#include <iostream>
using namespace std;

template <typename T>
class Base {
public:
void fun() {
cout << "Base::fun" << endl;
}

void doSomething() {
T* pT = static_cast<T*>(this); <== "由基类转换成派生类"的"逆"转换
pT->fun();
}
};

class Drive : public Base<Drive> {
public:
void fun() {
cout << "Drive::fun" << endl;
}
};

int main() {
Drive obj;
obj.doSomething();

return 0;
}
wishfly 2007-08-05
  • 打赏
  • 举报
回复
对于支持抛出COM异常的组件,需要ISupportErrorInfo接口
class ATL_NO_VTABLE CSample :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CParser, &CLSID_Parser>,
public ISupportErrorInfo,
public IDispatchImpl<IParser, &IID_IParser, &LIBID_sampleLib, /*wMajor =*/ 1, /*wMinor =*/ 0>
{
public:

组件的ISupportErrorInfo接口其实就一个方法InterfaceSupportsErrorInfo,用来判断组件中某一接口是否支持截获异常。
STDMETHODIMP CParser::InterfaceSupportsErrorInfo(REFIID riid)
{
static const IID* arr[] =
{
&IID_IParser
};

for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
if (InlineIsEqualGUID(*arr[i],riid))
return S_OK;
}
return S_FALSE;
}

也就是说:只有接口位于arr表中时,客户端才可以截获异常。

static const IID* arr[] =
{
&IID_IParser
};

总结:只有接口位于arr表中时,客户端才可以截获异常。但并不影响其在组件实现中抛出异常。

为什么要这么作呢?为了效率?
lbb2006 2007-08-05
  • 打赏
  • 举报
回复
mark
wishfly 2007-08-05
  • 打赏
  • 举报
回复
另外,使用 #import 等包装组件,组件有些方法通过返回值来传递[out, retval]参数,这样就不会返回HRESULT。
所以,当使用 #import 等包装组件时,必须通过try{}catch{},来捕获错误。
wishfly 2007-08-05
  • 打赏
  • 举报
回复
另外,使用 #import 等包装组件,组件有些方法通过返回值来传递参数,这样就不会返回HRESULT。
所以,当使用 #import 等包装组件时,必须通过try{}catch{},来捕获错误。
wishfly 2007-08-05
  • 打赏
  • 举报
回复
这些都是编译器作的手脚。十分巧妙!
PF!
lbb2006 2007-08-05
  • 打赏
  • 举报
回复
mark
wishfly 2007-08-05
  • 打赏
  • 举报
回复
其实在客户端可以得到的信息就是:HRESULT(_hr)和GetErrorInfo
经MS包装后,抛出异常的过程是这样的:
if (FAILED(_hr){
使用GetErrorInfo得到的信息,来填充_com_error
然后抛出 _com_error
}
加载更多回复(26)

3,245

社区成员

发帖
与我相关
我的任务
社区描述
ATL,Active Template Library活动(动态)模板库,是一种微软程序库,支持利用C++语言编写ASP代码以及其它ActiveX程序。
社区管理员
  • ATL/ActiveX/COM社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧