C++基础问题!对指针理解比较深的高手和精通COM的专家请进!(菜鸟勿进)嫌分不够的话可以重开帖子另给!

GoogleGeek 2002-05-10 07:17:11
小弟正在学习COM在历届接口的查询时陷入了万劫不复之地!请高手指点迷津!
下面是我的一个简单模拟与分析,程序很简单!/
/ ComTest.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include"iostream.h"

enum UUID {IID_IANIMAL=100,IID_ICANJUMP,IID_ICANFLY};
typedef unsigned int UINT;

// Base class for demo
class IAnimal
{
public:
virtual bool QueryInterface(UINT nID,void **ppvObject)=0;
virtual void AddRef()=0;
virtual void Release()=0;
};
class ICanJump:public IAnimal
{
public :
//IAniaml methods
virtual bool QueryInterface(UINT nID,void **ppvObject)=0;
virtual void AddRef()=0;
virtual void Release()=0;

virtual void Jump()=0;
};
class ICanFly:public IAnimal
{
public:
//IAnimal methods
virtual bool QueryInterface(UINT nID,void **ppvObject)=0;
virtual void AddRef()=0;
virtual void Release()=0;

virtual void Fly()=0;
};

class MixAnimal:public ICanJump,public ICanFly
{
private:
long m_lRef;
public:
MixAnimal():m_lRef(0) { }
~MixAnimal() { }
//IAnimal methods
virtual bool QueryInterface(UINT nID,void **ppvObject);
virtual void AddRef();
virtual void Release();

//ICanJump methods
virtual void Jump();

//ICanFly methods
virtual void Fly();
};
bool MixAnimal::QueryInterface(UINT nID,void **ppvObject)
{
switch(nID)
{
case IID_IANIMAL:
*ppvObject=static_cast<ICanJump*>(this);
break;
case IID_ICANJUMP:
*ppvObject=static_cast<ICanJump*>(this);
break;
case IID_ICANFLY:
*ppvObject=static_cast<ICanFly*>(this);
break;
default:
*ppvObject=NULL;
return false;
break;
}

reinterpret_cast<IAnimal*>(*ppvObject)->AddRef();
return true;
}
void MixAnimal::AddRef()
{
m_lRef++;
}
void MixAnimal::Release()
{
m_lRef--;
if(m_lRef==0)
delete this;
}
void MixAnimal::Jump()
{
cout<<"Hi, I can jump very high! !"<<endl;
}
void MixAnimal::Fly()
{
cout<<"Hi,I can fly very high!"<<endl;
}
int main(int argc, char* argv[])
{
IAnimal *pIAnimal=NULL;
ICanJump *pICanJump=NULL;
ICanFly *pICanFly=NULL;

MixAnimal *pMixAnimal=new MixAnimal;

pMixAnimal->QueryInterface(IID_IANIMAL,reinterpret_cast<void**>(&pIAnimal));
//Output the value of pIAnimal pointer
cout<<pIAnimal<<endl;
//First search the Interface of ICanJump
if(pIAnimal!=NULL)
{
if(pIAnimal->QueryInterface(IID_ICANJUMP,reinterpret_cast<void**>(&pICanJump))&&pICanJump!=NULL)
{
//Output the value of pICanIump pointer
cout<<pICanJump<<endl;
pICanJump->Jump();
pICanJump->Release();
}
}
//Secondly search the interface of ICanFly
if(pIAnimal->QueryInterface(IID_ICANFLY,reinterpret_cast<void**>(&pICanFly))&&pICanFly!=NULL)
{
//Output the value of pICanFly pointer
cout<<pICanFly<<endl;
pICanFly->Fly();
pICanFly->Release();
pICanFly->QueryInterface(IID_ICANJUMP,reinterpret_cast<void**>(&pICanJump));
//Output the value of pICanJump pointer
cout<<pICanJump<<endl;
pICanJump->Release();

}

pMixAnimal->Release();

return 0;
}

下面是我的一个输出结果:
//Output the value of pIAnimal pointer
0x004102E0
Hi, I can jump very high!
0x004102E4
Hi,I can fly very high!
0x004102E0

//个人分析:
由于用到了虚函数使得类MixAniaml的内存分布可能为:
已经省略掉了该类所关联的type_info object.

----------->pfnQueryInterface
| pfnAddRef
| pfnRelease
| pfnJump
this -----> vptrCanJump--
vptrCanFly--------------->pfnQueryInterface
m_lRef pfnAddRef
pfnRelease
pfnFly
显然输出结果:(不同的机器的输出结果可能不同!)
0x004102E0 应该为指针vptrCanJump的值,而0x004102E4因该为指针vptrCanfly的值
我的问题出现在函数bool MixAnimal::QueryInterface(UINT nID,void **ppvObject)上面
该函数在实现时使用了强制类型转换,
.......
switch(nID)
{
case IID_IANIMAL:
*ppvObject=static_cast<ICanJump*>(this);
break;
case IID_ICANJUMP:
*ppvObject=static_cast<ICanJump*>(this);
break;
case IID_ICANFLY:
*ppvObject=static_cast<ICanFly*>(this);
break;
}
........
我想知道的是在我的程序中用四次改函数的调用,在每一调用时函数里面的this指针是怎么传递进去的?
我的理解是该函数应该被编译器解释成下面的样子:
bool MixAnimal::QueryInterface(MixAnimal *this,UINT nID,void **ppvObject)
而函数调用:
pMixAnimal->QueryInterface(IID_IANIMAL,reinterpret_cast<void**>(&pIAnimal));
应该被转化为:
(*pMixAnimal-vptrCanJump[1])QueryInterface(pMixAnimal,IID_IANIMAL,reinterpret_cast<void**>(&pIAnimal));
也就是说将最左边的类指针作为this指针传递进去!
其他的函数调用也是这样,但是这样就出现了一个奇怪的问题:我们可以通过类型强制转化从一个整体得到局部
(这很正常),但是我们却不能够从一个局部通过类型转换得到整体!但是事实上我们却做到了!why?
我的意思是:
我在第一次调用时将pMixAniaml作为this指针,通过类型转化可以很容易的得到pAnimal,因为pAnimal可以看成
this所指的对象的一个局部(在该函数的具体的实现中可以看出来!)
我在第二次用pAnimal调用函数时,也应该很容易的得到pCanJump的值,因为在我的实现中他们两者实际上指向同一块地址。
我在第三次用pCanJump调用函数时,也因该正确地得到pCanFly的值,因为这还是由整体得到局部。
强制类型转换的结果使得pCanJump和pCanFly的值不相等!
但是在第四次由pCanFly调用函数时,却不应该正确地得到pCanJump的值,因为这可以看成有一个局部获得整体或者有一个局部获得另一个局部!

真是匪夷所思!
Who Can help me?


...全文
116 17 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
firecoffee 2002-07-04
  • 打赏
  • 举报
回复
jl
GoogleGeek 2002-07-04
  • 打赏
  • 举报
回复
嗨!自己的基础真是太差,现在终于明白了类的虚函数指针的转换原理!
谢谢各位的关注!
CppCore 2002-05-13
  • 打赏
  • 举报
回复
咳,大虾也是从才鸟过来的
singlerace 2002-05-13
  • 打赏
  • 举报
回复
这是多重继承下的虚拟函数问题。你可以看看“深度探索C++对象模型”一书。
MixAnimal的内存布局如下

----------->MixAnimal::QueryInterface
| MixAnimal::AddRef
| MixAnimal::Release
| MixAnimal::Jump
this -----> vptrCanJump--
vptrCanFly--------------->thunk of MixAnimal::QueryInterface
m_lRef thunk of MixAnimal::AddRef
thunk of MixAnimal::Release
MixAnimal::Fly
vptrCanFly中实际是一些thunk的地址,其作用就是转换this指针
寂寞漂泊 2002-05-12
  • 打赏
  • 举报
回复
我来看看
programcat2001 2002-05-11
  • 打赏
  • 举报
回复
"菜鸟勿进" 是哪个朝代的成语? 有谁知道,我怎么不懂什么意思就进来了!
GoogleGeek 2002-05-11
  • 打赏
  • 举报
回复
在此我向所有被我伤害了感情的CSDN上的朋友道歉,我也是一位菜鸟,谢谢大家的批评与指正!
GoogleGeek 2002-05-11
  • 打赏
  • 举报
回复
在此我向所有被我伤害了感情的CSDN上的朋友道歉,我也是一位菜鸟,谢谢大家的批评与指正!
139129 2002-05-11
  • 打赏
  • 举报
回复
学习!
ultraboy 2002-05-11
  • 打赏
  • 举报
回复
这不是整体局部的问题,而是有了类型定义以后,编译器就知道几个vtable之间的偏移,所以由任意一个指针,都可以加上vtable之间的偏移都可以得到其他的vtable的指针。
xjl930 2002-05-10
  • 打赏
  • 举报
回复
你很牛吗?“菜鸟勿进”,那你进来干什么了,最烦看到像你这样的帖子。

小伙子,谦虚一点好!

难听的话我也就不多说了,自己好好反省反省!

GoogleGeek 2002-05-10
  • 打赏
  • 举报
回复
我已经测试过了,你说的内存布局不对!
在VC中
sizeof (MixAnimal)=12;
并且,两个指向虚函数表的指针是紧靠在一起的!也只有一个m_lRef变量
如果按照你的内存布局则:sizeof (MixAnimal)至少应该等于16(因为每个数据占四个字节)!
请指正!
139129 2002-05-10
  • 打赏
  • 举报
回复
首先IMixAnimal的布局应该是这个样子:

this -----> vptrCanJump-------------->pfnQueryInterface
pfnAddRef
pfnRelease
ICanJump::pfnJump
ICanFly::pfnFly
~~~~~~~~~~~~~~~~
m_lRef

vptrCanFly--------------->pfnQueryInterface
m_lRef pfnAddRef
pfnRelease
pfnFly

所以
————————————————————————————————
this -----> vptrCanJump-------------->pfnQueryInterface
pfnAddRef
pfnRelease
第一次和第四次都是调用这里 ———> ICanJump::pfnJump
————————————————————————————————
第三次是调用这里———> ICanFly::pfnFly
____________________________________________________________
m_lRef

vptrCanFly--------------->pfnQueryInterface
m_lRef pfnAddRef
pfnRelease
pfnFly

自己的一点点想法
GoogleGeek 2002-05-10
  • 打赏
  • 举报
回复
难道就没有人停下来仔细的把帖子看完,程序真的很简单,就是模拟了一下Com!
我只对于其中的就扣查询的实现由问题而已!
GoogleGeek 2002-05-10
  • 打赏
  • 举报
回复
这只是简单的C++语言的实现机制问题,可以不用COM ,我想知道C++ 如何实现指针的内存管理,与不同的指针类型之间的转化内幕!
zhakewei 2002-05-10
  • 打赏
  • 举报
回复
建议看一下essential com
GoogleGeek 2002-05-10
  • 打赏
  • 举报
回复
up!up!!up!!!

16,548

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • AIGC Browser
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

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