原文:http://www.haogongju.net/art/1694028
多繼承時,父類指針指向子類對象時,父類指向的不是子類開始地址,而是子類中對應父類的結構的類對象的基地址,所以,當多個父類指向同一個子類時,父類的指針值其實是不一樣的。
5.觀察下面一段代碼:
class ClassA
{
public:
virtual ~ ClassA(){};
virtual void FunctionA(){};
};
class ClassB
{
public:
virtual void FunctionB(){};
};
class ClassC : public ClassA,public ClassB
{
public:
};
ClassC aObject;
ClassA* pA=&aObject;
ClassB* pB=&aObject;
ClassC* pC=&aObject;
關於pA,pB,pC的取值,下面的描述中正確的是:
A.pA,pB,pC的取值相同. B.pC=pA+pB
C.pA和pB不相同 D.pC不等於pA也不等於pB
這個是多重繼承的問題。在《Thinking in C++》第二卷的363頁看到重複子對象這部分的內容,明白了怎麼回事。
以上兩個基類均含有虛函數,故其大小至少有一個虛函數表的指針大小,結構大概是這樣的:
ClassA類數據成員 |
ClassB類數據成員 |
ClassC類其他數據成員 |
可以看出對象aObject以ClassA子對象開頭,然後是ClassB子對象部分,最後的部分來自ClassC類本身。ClassC的對象既是一個ClassA也是一個ClassB。
故aObject可以向上類型轉換到兩者中任何一個基類型。
當向上類型轉換爲ClassA時,結果指針指向ClassA子對象部分,剛好在ClassC對象的開始位置,所以地址pA和pC是相同的。
當向上類型轉換到ClassB時,結果指針必須指向ClassB子對象所在的實際位置,因爲類ClassB並不知道有關ClassC的事情。
所以上題的答案應爲C。
但是這個地方有個好玩的現象:
如果添加如下代碼:cout<<"pC==pB?"<<boolalpha<<(pC==pB)<<endl;
則返回值永遠都是true,儘管他們的地址輸出的時候並不相同。因爲pC在pC==pB的過程中被隱式轉換爲ClassB*,以使得比較有意義。
class a
{
public:
int aa
};
class b:public a
{
public:
int bb;
}
從內存的來看
如a
---------|
|佔一個int數據大小--|
|----(aa數據)------|
|---------
而b則是
---------|---------
|佔一個int數據大小--|佔一個Int數據大小--|
|從a中繼承而來------|---(bb數據----------|
|------------------
當定義一個基類類型的指針時
a *p;這時,這個指針指向的是a類型的數據
當p指針指向派生類的時候,因爲p是a類型的指針,所以*p只解釋爲a類型數據的長度,即
————————-|---------
|佔一個int數據大小--|佔一個Int數據大小--|
|從a中繼承而來------|-----(bb數據)-------|
|------------------
|------------|------------|
|-p只指向這個區域_--|
因此,當基類的指針(P)指向派生類的時候,只能操作派生類中從基類中繼承過來的數據。
指向派生類的指針,因爲內存空間比基類長,會導致嚴重了後果,所以不允許派生類的指針指向基類。而基類的指針可以指向派生類。
C++的多態性能解決基類指針不能操作派生類的數據成員的問題。
用C++比較好說明白:
1:指針的可訪問性是由指針的定義決定的,比如說用BaseClass定義的指針,可訪問的範圍就是BaseClass的內存區域
2:允許用一個指向基類的指針指向派生類,由於被指向的對象的內存空間大於指針的可訪問空間,所以這種向上映射是安全的
3:對象在調用虛函數的時候,是調用父類的函數還是調用派生類的函數,是和對象的類型有關的,比如說一個派生類B,其父類是A,則B的對象調用父類中被聲明爲VIRTUAL的函數時,被B 所OVERRIDE的函數調用的是B裏的函數,而B沒有OVERRIDE的函數調用的是基類裏的函數