#include <iostream>
using namespace std;
//一组标记函数
void funT()
{
cout<<"T"<<endl;
}
void funA()
{
cout<<"A"<<endl;
}
void funB()
{
cout<<"B"<<endl;
}
void funC()
{
cout<<"C"<<endl;
}
typedef void ( *pVf )();
pVf exPf[4] = { &funT, &funA,&funB,&funC } ;
class CT
{
public :
CT()
{
tDat = &exPf[0] ; //指向虚表元素,模拟虚表指针VPTR
cout<<"CT()"<<endl;
}
~CT() { cout<<"~CT()"<<endl; }
private:
pVf * tDat;
};
class CA
{
public :
CA()
{
aDat = &exPf[1] ;
cout<<"CA()"<<endl;
}
virtual ~CA();
private:
pVf * aDat;
};
CA::~CA()
{
cout<<"~CA()"<<endl;
}
class CB:public CA
{
public :
CB()
{
bDat = &exPf[2] ;
cout<<"CB()"<<endl;
}
~CB() { cout<<"~CB()"<<endl; }
private:
pVf * bDat;
CT bCt;
};
int main()
{
CA * pa = new CB[2];
delete []pa;
return 0;
}
using namespace std;
//一组标记函数
void funT()
{
cout<<"T"<<endl;
}
void funA()
{
cout<<"A"<<endl;
}
void funB()
{
cout<<"B"<<endl;
}
void funC()
{
cout<<"C"<<endl;
}
typedef void ( *pVf )();
pVf exPf[4] = { &funT, &funA,&funB,&funC } ;
class CT
{
public :
CT()
{
tDat = &exPf[0] ; //指向虚表元素,模拟虚表指针VPTR
cout<<"CT()"<<endl;
}
~CT() { cout<<"~CT()"<<endl; }
private:
pVf * tDat;
};
class CA
{
public :
CA()
{
aDat = &exPf[1] ;
cout<<"CA()"<<endl;
}
virtual ~CA();
private:
pVf * aDat;
};
CA::~CA()
{
cout<<"~CA()"<<endl;
}
class CB:public CA
{
public :
CB()
{
bDat = &exPf[2] ;
cout<<"CB()"<<endl;
}
~CB() { cout<<"~CB()"<<endl; }
private:
pVf * bDat;
CT bCt;
};
int main()
{
CA * pa = new CB[2];
delete []pa;
return 0;
}
/*Linux/GCC输出结果为:
CA()
CT()
CB()
CA()
CT()
CB()
B //编译器析构pa[1],没有调用正确的析构函数
~CB() //编译器析构pa[0]
~CT()
~CA()
从结果中可以看出:
1:GCC编译器对虚表指针VPTR的摆放,是放在对象空间的起始处的.
2:析构对象的顺序与构造时相反.
3:类CA的内存布局:VPTR, aDat ;类CB的内存布局:VPTR, aDat,(即CA布局), bDat, tDat(CT布局)
4:执行delete []pa;操作时
4.1:首先析构对象pa[1], 即计算pa[1]的地址,采用基址+偏移(编译期常量,此处是sizeof( CA ))
4.2:根据多态性调用VPTR指向的虚表中的函数
4.3:如果调用到CB::~CB(),则还会调用成员对象的析构函数和基类对象的析构函数.
4.4:由于目标对象地址的计算方式编译期就确定,因此与实际情况不符,从而出错.
P.S.这个例子在VC编译器上能正确运行,是因为VC编译器为对象数组的构造和析构做了特殊处理~