大家對虛表並不陌生,都知道每個含有虛函數的類對象都有1個虛指針,但是在現實使用中,卻總是因為這而調試半天,才發現原來是虛指針惹的禍。我這幾天在調試代碼時候也中招了,我的問題是這樣的,如下圖,CTree是最底層基類(非虛類), CSamplerTree(虛類)派生自CTree,CMSamplerTree,CASamplerTree派生自CSamplerTree,
CTree中包括兩個成員變量,QList <CTree *> childList;樹中有多少個孩子節點;CTree *parent;當前樹節點的父親節點,程序中我大量使用CTree *pTree指針指向CSamplerTree、CMSamplerTree、CASamplerTree ,從而達到統一處理的目的,從而使代碼很簡潔,復用性高。但是誰曾想到,程序一運行就會崩潰,通過調試發現,CSamplerTree、CMSamplerTree、CASamplerTree的指針當指向CTree的指針時,地址均加了4,為什麼呢?為了加深理解,我做了一個簡單的測試代碼:
[cpp]
#include <stdio.h>class CBase {
public:
CBase() {}
void func()
{
printf("base\n");
}
};
class CDerived : public CBase {
public:
CDerived() {}
virtual void func1()
{
printf("derived\n");
}
};
void main()
{
CBase *pBase = new CDerived();
pBase->func();
CDerived *pDerived = (CDerived *)pBase;
printf("%d %d\n", pDerived, pBase);
pDerived->func();
CBase *pBase1 = new CBase();
pBase1->func();
CDerived *pDerived1 = (CDerived *)pBase1;
printf("%d %d\n", pDerived1, pBase1);
pDerived1->func();
}
#include <stdio.h>class CBase {
public:
CBase() {}
void func()
{
printf("base\n");
}
};
class CDerived : public CBase {
public:
CDerived() {}
virtual void func1()
{
printf("derived\n");
}
};
void main()
{
CBase *pBase = new CDerived();
pBase->func();
CDerived *pDerived = (CDerived *)pBase;
printf("%d %d\n", pDerived, pBase);
pDerived->func();
CBase *pBase1 = new CBase();
pBase1->func();
CDerived *pDerived1 = (CDerived *)pBase1;
printf("%d %d\n", pDerived1, pBase1);
pDerived1->func();
}下面是輸出的結果,從結果可以看出派生類指針指向基類指針,指針地址會加4,基類指針指向派生類時,指針地址會減4。
base
200672 200676
derived
base
200740 200744
Press any key to continue
下面我們看看派生類對象和基類對象的內存是如何組織的,我們在上例的基礎上引入2個變量,代碼如下:
[cpp]
#include <stdio.h>class CBase {
public:
CBase() {}
void func()
{
printf("base\n");
}
int a;
};
class CDerived : public CBase {
public:
CDerived() {}
virtual void func1()
{
printf("derived\n");
}
int b;
};
void main()
{
CBase *pBase = new CDerived();
CDerived *pDerived = (CDerived *)pBase;
printf("%d %d\n", pDerived, pBase);
printf("%d %d %d\n", &pDerived->a, &pDerived->b, &pBase->a);
}
#include <stdio.h>class CBase {
public:
CBase() {}
void func()
{
printf("base\n");
}
int a;
};
class CDerived : public CBase {
public:
CDerived() {}
virtual void func1()
{
printf("derived\n");
}
int b;
};
void main()
{
CBase *pBase = new CDerived();
CDerived *pDerived = (CDerived *)pBase;
printf("%d %d\n", pDerived, pBase);
printf("%d %d %d\n", &pDerived->a, &pDerived->b, &pBase->a);
}
200672 200676
200676 200680 200676
Press any key to continue
從輸出結果我們可看出,CDerived對象的起始地址存放的是虛表指針vptr,接下來的是基類的成員變量,接下來再是自身的成員變量。