之前学C++的时候了解到,其中很多控件是基于com的,今天初学接口,写了一小段测试程序
接口为IX,IY
组件为CA,(继承于IX和IY)
客户为main函数
流程如下:
接口连接组件的功能是由虚函数实现的
一般来说,父类作为接口,子类作为组件,子类实现父类的功能(组件实现接口所要求的功能)
虚函数就是实现这一过程的方法
对于虚函数在内存中的分配,下面是个小测试程序:
1 #include2 using namespace std; 3 class CA 4 { 5 public: 6 int i; 7 virtual void __stdcall virtualFun()=0; 8 }; 9 10 class CASub:public CA11 {12 virtual void __stdcall virtualFun()13 {14 return;15 }16 };17 18 class CB19 {20 public:21 int i;22 };23 24 void main()25 {26 CASub *pA = new CASub;27 CB *pB = new CB;28 cout<<"pA Address:"< < i)< i)<
运行结果如下图
由此可见pA(由CASub产生,CASub又是由CA继承而来)的长度为8个字节,pA中有虚函数和int类型
pB(由CB产生)的长度为4个字节,其中pB中只有int类型,因此知道虚函数占用的是4个字节,
不过虚函数中并没有数据,因此该4个字节应该是虚函数的地址,
另外,程序中虚函数是写在int后的,而到了内存中虚函数却在前面,由此可以判定是编译器故意为之,为了提高虚函数调用的速度。
然后我们来看一下接口地址和组件地址的关系,
组件是CA,接口为IX和IY,
测试代码如下
1 #include2 #include 3 using namespace std; 4 void trace(const char* pMsg) 5 { 6 cout< < Fx1();43 pIX->Fx2();44 45 IY* pIY=pA;46 trace("Client:Use the IY interface.");47 pIY->Fy1();48 pIY->Fy2();49 50 51 cout<
运行结果
CA继承于IX和IY,由运行结果可以看出pA地址跟pIX一致,这也是继承的特性,构造CA的时候会先构造父类IX,然后构造IY;
组件CA在内存中的大小为8字节,接口各4个字节,而接口作为内部有虚函数的类,虚函数地址就占了4个字节,并且每个接口有两个函数,
据书上的说法,虚函数的实现是,内存中首个4BYTE包含的是指向虚函数表的指针,然后再在表中包含指向实现该虚函数的实际内存地址,如下图:
注:接口的内存结构对于不同的操作系统可能是不同的,例如在mac上,vtbl指针将指向一个伪指针,而第一个函数指针则位于第二个表项中
假设子类不含有虚函数Fx1,那么就会把父类的虚函数的入口地址写到子类虚函数表的Fx1处,如果子类存在虚函数Fx1,那么该处就填子类Fx1的入口地址,
对于所有不含有Fx1的子类,而父类有Fx1,那么所有子类将共用该函数的代码段
在学习过程中,本人发现了类中的静态数据类型(static)必须在外部重新定义一遍
如:
class A{public: static int i;};
手动定义一遍,int A::i=0;这个时候才会进行 i 的内存分配
另外还有一个问题是上面的这条int A::i=0;无法放在main 函数中,原因估计是作为类中静态变量,同时也是全局变量,必须时刻都在内存中
由于水平有限,上面部分为推测,如有错误欢迎留言纠正