类之间强制转换后虚函数的调用问题,不知道怎么描述,进来细看INonDelegatingUnknown接口与IUnknow接口定义
类之间强制转换后虚函数的调用问题,不知道怎么描述,进来细看
INonDelegatingUnknown接口与IUnknow接口
定义基本类似 定义如下
C/C++ codeinterface IUnknown { virtual HRESULT QueryInterface(REFIID iid,void **ppv); virtual ULONG AddRef(void); virtual ULONG Release(void); };interface INonDelegatingUnknown{ virtual HRESULT NonDelegatingQueryInterface (REFIID riid, LPVOID *ppv) ; virtual ULONG NonDelegatingAddRef(void) ; virtual ULONG NonDelegatingRelease(void) ;};
这样两个函数定义都很相似的两个类可以不可以理解成:
虚函数定义完全相同的两个类,其对象的虚函数表结构也完全相同?
然后问题来了
我定义了这样两个类
C/C++ codeclass A{public: virtual void method1() { cout << "This is A`s method1()"<<endl; } virtual void method2() { cout << "This is A`s method2()"<<endl; } virtual void method3() { cout << "This is A`s method3()"<<endl; }};class CallA{public: virtual void callMethod1() = 0; virtual void callMethod2() = 0; virtual void callMethod3() = 0;};
现在我通过实例化一个A 并且创建一个CallA指针
通过强制转换 将A转化为一个CallA指针
C/C++ code A testA; CallA *pCallA = (CallA *)(&testA);
然后我通过pCallA调用自身并不存在的方法。可以打到调用A中已经实现好的方法的目的
C/C++ codepCallA->callMethod1();
请问为什么会有这种使用方法?应用在什么场合?为什么要这样用。
我实在看和COM有关的文档里看到这样一句代码联想到的这个
C/C++ codeCMyComponent::CMyComponent(IUnknown *pOuterUnkown){ if (pOuterUnknown == NULL) [color=#FF0000] m_pUnknown = (IUnknown *)(INonDelegatingUnknown *)this;[/color] else m_pUnknown = pOuterUnknown; [ ... more constructor code ... ]}
红色部分
CMyComponent继承了INonDelegatingUnknown 接口
将this指针通过两级转换 使this指针先指向CMyComponent内的INonDelegatingUnknown 部分
再转换为IUnknown类型。
具体调用的时候
通过m_pUnknown -> QueryInterface()
可能调用到INonDelegatingUnknown内的NonDelegatingQueryInterface方法
也可能调用到IUnknown内的QueryInterface方法
[解决办法]1.虚函数表是属于类的,每个类都有一个虚函数表
只要是不同的类,哪怕类里面的虚函数标志完全相同,也是属于不同的虚函数表,编译后被解析为不同类的虚函数,参考深度探索C++对象模型 函数语意学
2.指针转型只是影响 被指出的内存的大小和其内容
CallA *pCallA = (CallA *)(&testA);
拿你这句来说,CallA *pCallA 编译时期就确定了要指出的内存大小(内容不确定)
(CallA *)(&testA); 这个转型的作用是改变&testA这个指针所指出的内存大小,改为pCallA需要指出的内存大小
正个语句的意思就是,将首地址赋给pCallA ,在&testA指针指出的内存上扩充或者切割,以适应pCallA 所需要指出的内存大小
事实上pCallA->callMethod1();
这种调用没有定义
[解决办法]首先 你这么强制转换是错误的 类之间的强制转换只保证将子类的指针强制转换成基类 时的正确性
其他所有的自定义类之间的强制转换都不保证其正确性 行为视编译器而定
规则就在那里 为啥都喜欢违反规则呢?
另外你上面的问题建议去仔细的看下 虚函数表的实现
有2本书里有详细说明 深度探索C++面向对象模型 和 C++设计与演化
[解决办法][解决办法][解决办法]这种问题真是蛋疼啦
在栈结构上
CallA *pCallA = (CallA *)(&testA);
这个语句就是个模型替换,这个替换分扩充替换和切割替换,决定于替换对象模型与被替换模型的大小
这种替换没有意义,替换后,内存空间里还残留着被替换对象的数据值,数据按字节以及声明次序依次占据空间。结果导致CallA 类的vptr的值是原来对象testA的vptr的值,这个指针指向的是A类虚表地址,更要命的是:编译器寻址虚函数是很笨的,在编译的时候只记住了每个虚函数的索引,比如说callMethod3();他只会机械的去索引为2的位置,然后把函数解析出来。
这个时候调用虚函数
C/C++ code|-------|<<--pCallA A类的虚函数表|vptr --| ------->> |------||data1 | 0| vf1 ||data2 | 1| vf2 ||data3 | 2| vf3 | |.... | |------||-------||