(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)
参考文档:
http://blog.csdn.net/hgy413/article/details/6716397
http://blog.csdn.net/lbhli/article/details/6797145
前注:
删除类的编译翻译,例如delete pItem; 会翻译成
1. pItem::~析构函数(); // 调析构
2. operator delete(pItem); // 调类对象所占内存清理(大小sizeof(类))
如果pItem的类的析构为虚函数时,翻译成
1. 调用虚函数表中的析构函数,一层一层调用析构函数
2. 重设删除地址 (猜测,基于上一步指向虚函数的实际类型,对指针重设:类似 CChlid继承自CItem, CChild* pChild = (CChild*)pItem,从而指针与CItem指针偏移差考虑进来)
3. operator delete(删除地址);
class IBase { public: }; class CItem1 : public IBase { public: virtual void A(){} }; int main() { IBase* pBase = CItem1(); delete pBase; }
添加调测代码,查输出的话,可以看到父类指向的地址偏移了四位(虚函数表的空间)
输出:
pItem=0x5d0610, sizeof(*pItem)=4
pBase=0x5d0614, sizeof(*pBase)=1
#define ViewPointerAddress(pVal) ViewPointerAddressFunc(#pVal, pVal) template <typename T> void ViewPointerAddressFunc(char* pCharName, T* pPointer) { char sLog[256]; sprintf_s(sLog, "%s=0x%x, sizeof(*%s)=%d\n", pCharName, pPointer, pCharName, sizeof(*pPointer)); printf(sLog); OutputDebugStringA(sLog); } ... ViewPointerAddress(pItem); ViewPointerAddress(pBase); ...
报错在HeapValide异常这一块,为什么会报错呢:
进一步按照debug的方法,添加调试代码,定位可以看到:
_CrtMemBlockHeader *pBaseHdr = pHdr(pBase);
_CrtMemBlockHeader *pItemHdr = pHdr(pItem);
HeapValidate(GetProcessHeap(), 0, pHdr(pItem));
HeapValidate(GetProcessHeap(), 0, pHdr(pBase));
报错在 HeapValidate(GetProcessHeap(), 0, pHdr(pBase)) 这句话上,从结构上看:pBaseHdr中是有些异常的
分析:
每个内存分配前面都放有一个 _CrtMemBlockHeader 结构,但像上面的情况,由于pBase的地址偏移了4位,导致获取它前面的 _CrtMemBlockHeader 存在异常
所以HeapValidate鉴权失败,崩溃退出
多重继承虚函数时,也有类似的问题,例如:
class A{ public: virtual void f1()=0;};
class B{ public: virtual void f2()=0;};
class C : public A, public B {}
C* pC = new C();
B* pB = pC;
delete pB;
此时如果新建C对象,使用B指向他的时候,地址会偏移4字节,delete使用B指针的话导致异常;使用A指针的话,由于编译器把他放在前面,它的问题不大,不会崩溃。
pItem=0x4a02f0, sizeof(*pItem)=8
pBaseA=0x4a02f0, sizeof(*pBaseA)=4
pBaseB=0x4a02f4, sizeof(*pBaseB)=4
其实对于多重继承,有没有虚函数,使用非第一个继承类删除都会有问题,例如:
class A{ public: int a;};
class B{ public: int b;};
class C : public A, public B {int c;}
C* pC = new C();
B* pB = pC;
delete pB;
查看指针的话,会看到内存分布:A|B|c-内部变量
(转成A的时候,删除没有问题;转成B删除就会有问题)
(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)