C++基礎類別和派生類指標的相互賦值和轉換

2020-07-16 10:04:20
在公有派生的情況下,派生類的指標可以直接賦值給基礎類別指標。但即便基礎類別指標指向的是一個派生類的物件,也不能通過基礎類別指標存取基礎類別沒有而派生類中有的成員。

基礎類別的指標不能賦值給派生類的指標。但是通過強制型別轉換,也可以將基礎類別指標強制轉換成派生類指標後再賦值給派生類指標。只是在這種情況下,程式設計師需要保證被轉換的基礎類別指標本來就指向一個派生類的物件,這樣才是安全的,否則很容易出錯。

下面的程式碼演示了基礎類別和派生類指標的互相轉換:
#include <iostream>
using namespace std;
class CBase
{
protected:
    int n;
public:
    CBase(int i) :n(i) { }
    void Print() { cout << "CBase:n=" << n << endl; }
};
class CDerived :public CBase
{
public:
    int v;
    CDerived(int i) :CBase(i), v(2 * i) { }
    void Func() { };
    void Print()
    {
        cout << "CDerived:n=" << n << endl;
        cout << "CDerived:v=" << v << endl;
    }
};
int main()
{
    CDerived objDerived(3);
    CBase objBase(5);
    CBase * pBase = &objDerived; // 使得基礎類別指標指向派生類物件
                                 //pBase->Func(); //錯, CBase類沒有Func()成員函數
                                 //pBase->v = 5;  //錯 CBase類沒有v成員變數
    pBase->Print();
    cout << "1)------------" << endl;
    //CDerived * pDerived = & objBase; //錯,不能將基礎類別指標賦值給派生類指標
    CDerived * pDerived = (CDerived *)(&objBase);
    pDerived->Print();  //慎用,可能出現不可預期的錯誤
    cout << "2)------------" << endl;
    objDerived.Print();
    cout << "3)------------" << endl;
    pDerived->v = 128;  //往別人的空間裡寫入資料,會有問題
    objDerived.Print();
    return 0;
}
在 Dev C++ 4.9.9.2 下的執行結果:
CBase:n=3
1)------------
CDerived:n=5
CDerived:v=5
2)------------
CDerived:n=3
CDerived:v=6
3)------------
CDerived:n=128
CDerived:v=6

第 27 行使得基礎類別指標 pBase 指向派生類物件 objDerived,這是合法的。執行完此行後,雖然 pBase 指向的是派生類物件,但是第 28 行如果不注釋掉,編譯是會出錯的,因為 CBase 類並沒有 Func 成員函數。

同理,第 29 行若不注釋掉也會出錯。

第 30 行,儘管基礎類別和派生類都有 Print 成員函數,而且 pBase 指向的是派生類物件,本行執行的依然是基礎類別的 Print 成員函數,產生第一行輸出。

總之,編譯器看到的是哪個類的指標,那麼就會認為要通過它存取哪個類的成員,編譯器不會分析基礎類別指標指向的到底是基礎類別物件還是派生類物件。

第 33 行,通過強制型別轉換,使得派生類的指標 pDerived 指向了基礎類別物件 objBase。第 34 行呼叫的 Print 成員函數就是 CDerived 類的 Print 成員函數。

這是有風險的語句。在 CDerived 物件中,成員變數 v 緊挨成員變數 n 存放,如圖 1 所示。


圖1:派生類物件的記憶體空間