Coder_Y_Jao 大牛进,我请教2个问题

liuzu2016 2012-09-26 12:16:49


你好我, 我翻了不少老帖子, 发现你说的一段话, 有些不懂,所以再请问

问题1:

Coder_Y_Jao在一个老帖子里说:

为了与语言无关的兼容性
_stdcall是大多数语言都支持的调用约定,_thiscall则用于c++的成员函数,需要ECX存储this。
你可能会问,COM都编译成二进制文件了,不都一样保证本身的功能正常,有什么关系呢。
考虑下面的情况,一个不支持_thiscall的语言中,调用某个成员函数参数入栈后,是不可能将this存储在ecx的。
而你编译好的COM(_thiscall)中,则使用这个ecx来间接操作其他成员等,这就导致了不兼容。
而_stdcall则直接push this指针当做参数入栈,COM中也是_stdcall,不需要关心那个ecx,通过栈来间接操作就好了。





如果有一个 com的接口函数内部调用了 _thiscall或者其他 函数调用约定的 函数,那么又如何呢?







问题2: com中的一个语法讨论


class B:public IOtherInterface
{
protected:

ULONG m_Ref;

B();

~B();

HRESULT _stdcall QueryInterface(const IID&iid, void** ppv);
HRESULT _stdcall AddRef();
HRESULT _stdcall Release();

//other interface members
HRESULT _stdcall Otherfunction()p;
HRESUTL Inti();

private:

IUnkown* m_pUnkownInner;


};





以下是 <com原理和应用 >一书提供的实现源码:



HRESULT B: QueryInterface(const IID& iid, void** ppv)
{
if(iid==IID_IUnkown)
{
*ppv=(IUnkown*)this; //转换(1) ,孙子类指针转换为祖父类指针
((IUnkown*)(*ppv)->AddRef();
}
else if(iid==IID_OtherInterface)
{
*ppv=(IOtherInterface*)this; //转换是否多余 (2)
((IOtherInterface*)(*ppv)->AddRef();

}
else if(iid== IID_SomeInterface)
{
return m_pUnkownInner->QueryInterface(iid,ppv);
}
else
{
*ppv=NULL;
return E_NONTERFACE;
}
return S_OK;
}


(1)为什么孙子类的指针可以强行转换为祖父类的, 我记得,只可以转换为父亲类的指针吧?

(2) 转换为父亲类的指针后,由于是虚函数,最终还是调用孙子类的 覆盖的虚函数, 所以 对于转换为(2)是否多余?




...全文
192 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
Coder_Y_Jao 2012-09-27
  • 打赏
  • 举报
回复
8楼推荐的书可以看一下,侯捷老师在开篇的时候说,C++对象模型是通往COM的一条途径
liuzu2016 2012-09-27
  • 打赏
  • 举报
回复
这个回答是错误的




正确的修正:

之所以可以转换成 祖父类指针,原因:与虚基类,非虚基类根本无关,是单线继承的,

原因就这么简单。





[Quote=引用 10 楼 的回复:]

似乎我懂了

虚继承首先会产生 虚基表,违背com理论, 所以不能够实用com

另外:虚继承 比较复杂,相对语法来说,比如:多个父类,有的可以覆盖虚函数,也的不覆盖, 反正较为复杂。

com采用非虚继承 ,指针转换 之所以成功的原因:

2个父亲类 都没有覆盖 祖父类的虚函数!!!

另外:也没有隐藏 来自 祖父类的非虚函数(当然,com中祖父类,父亲中不存在 隐藏,……
[/Quote]
liuzu2016 2012-09-27
  • 打赏
  • 举报
回复
似乎我懂了

虚继承首先会产生 虚基表,违背com理论, 所以不能够实用com

另外:虚继承 比较复杂,相对语法来说,比如:多个父类,有的可以覆盖虚函数,也的不覆盖, 反正较为复杂。

com采用非虚继承 ,指针转换 之所以成功的原因:

2个父亲类 都没有覆盖 祖父类的虚函数!!!

另外:也没有隐藏 来自 祖父类的非虚函数(当然,com中祖父类,父亲中不存在 隐藏,这是从c++语法讨论的)


谢谢你了啊






Coder_Y_Jao 2012-09-27
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 的回复:]
却是不能转换 3 个转换都失败了, 因为虚继承,

但是 com也没有采用虚继承,故意用一般的继承,这是公开的事情啊啊

为什么它能转换,而我则不能转
[/Quote]
不会3个都失败,只有这个会失败*ppv=(A*)this;
而*ppv=(B*)this;和*ppv=(C*)this;是转换到父类,是可以的。
你看到多出报错是因为你下面这句话的语法问题
*ppv->fun();
改成这样

if(id==_A)
{
*ppv=(A*)this;//这里报错
((A*)*ppv)->fun();//1

} else if(id==_B)
{
*ppv=(B*)this;//这里正常
((B*)*ppv)->fun();//2


}
else if(id==_C)
{
*ppv=(C*)this;//这里正常
((C*)*ppv)->fun();//3


}


它的能转换是因为它使用的是单一继承而不是多继承(菱形继承),不需要考虑是否虚拟继承的情况

class B:public IOtherInterface
赵4老师 2012-09-27
  • 打赏
  • 举报
回复
《深度探索C++对象模型》
liuzu2016 2012-09-27
  • 打赏
  • 举报
回复
为了验证你说的,

我重新写了个简单易懂的例子





#include <stdio.h>

enum ID{_A,_B, _C};

class A
{
public:
virtual void fun()=0;

};


//B ,C 都不是虚继承! 注意看代码
class B:public A
{
public:

void fun()
{
printf("B\n");
}
};

class C:public A
{
public:

void fun()
{
printf("C\n");
}
};

//D拥有2份A
class D:public B,public C
{
public:
void fun()
{
printf("D\n");
}

void QueryInterface( ID id, void** ppv)
{
if(id==_A)
{
*ppv=(A*)this;
(*ppv)->fun();

} else if(id==_B)
{
*ppv=(B*)this;
*ppv->fun();


}
else if(id==_C)
{
*ppv=(C*)this;
*ppv->fun();


}
}

};




却是不能转换 3 个转换都失败了, 因为虚继承,

但是 com也没有采用虚继承,故意用一般的继承,这是公开的事情啊啊

为什么它能转换,而我则不能转





[Quote=引用 6 楼 的回复:]

我再说明白一点吧,在没有虚拟继承的菱形继承中,孙子类拥有两份祖父类拷贝,是不能直接转换的。
你的例子中没有这样的情况,而COM的多继承就有这样的情况,比如我2楼的代码:
reinterpret_cast< IUnknown* >(*ppv)->AddRef();就不能换成下面的代码
static_cast< IUnknown* >(this)->AddRef();
[/Quote]
Coder_Y_Jao 2012-09-27
  • 打赏
  • 举报
回复
我再说明白一点吧,在没有虚拟继承的菱形继承中,孙子类拥有两份祖父类拷贝,是不能直接转换的。
你的例子中没有这样的情况,而COM的多继承就有这样的情况,比如我2楼的代码:
reinterpret_cast< IUnknown* >(*ppv)->AddRef();就不能换成下面的代码
static_cast< IUnknown* >(this)->AddRef();
liuzu2016 2012-09-26
  • 打赏
  • 举报
回复
继续顶起来,
liuzu2016 2012-09-26
  • 打赏
  • 举报
回复
啥叫截断,楼上,你说的概念怎么没听说过?
漫步者、 2012-09-26
  • 打赏
  • 举报
回复
为什么孙子类的指针可以强行转换为祖父类的, 我记得,只可以转换为父亲类的指针吧?
答:要么采取多态,要么是截断的方式,不是说只可以使父类指针。
Coder_Y_Jao 2012-09-26
  • 打赏
  • 举报
回复
问题1:内部调用_thiscall或其他约定不必遵守COM接口上的规范,与接口无关的细节只要符合c++本身的规范就好了。

问题2:
(1):你说得这个是在菱形继承中存在的问题。
(2):将this 转换为void*时最好先将其转换为相应的接口类型。在这个例子中不转换也可,但风格很差。
考虑下面这样的情况(多继承):

class CA : public IX, public IY
{
...
};

HRESULT __stdcall CA::QueryInterface(const IID& iid, void** ppv)
{
if(iid == IID_IUnknown)
{
*ppv = static_cast< IX* >(this);
}
else if(iid == IID_IX)
{
*ppv = static_cast< IX* >(this);
}
else if(iid == IID_IY)
{
*ppv = static_cast< IY* >(this);
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
reinterpret_cast< IUnknown* >(*ppv)->AddRef();
return S_OK;
}


首先,QueryInterface需要返回一个接口,static_cast< IX* >(this)和static_cast< IY* >(this)的值是不一样的,此时转换的意义就出来了。
liuzu2016 2012-09-26
  • 打赏
  • 举报
回复
(1)为什么孙子类的指针可以强行转换为祖父类的, 我记得,只可以转换为父亲类的指针吧?

(2) 转换为父亲类的指针后,由于是虚函数,最终还是调用孙子类的 覆盖的虚函数, 所以 对于转换为(2)是否多余?

64,643

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

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