C++:派生類指針賦值給基類指針問題

在C++繼承中,很容易遇到一個問題,那就是將派生類指針賦值給基類指針(向上轉型)的情況,下面我們就來舉例分析:

舉一個多繼承的例子:

#include <iostream>
using namespace std;

//基類A
class A {
public:
	A(int a);
public:
	void display();
protected:
	int m_a;
};
A::A(int a) : m_a(a) { }
void A::display() {
	cout << "Class A: m_a=" << m_a << endl;
}

//中間派生類B
class B : public A {
public:
	B(int a, int b);
public:
	void display();
protected:
	int m_b;
};
B::B(int a, int b) : A(a), m_b(b) { }
void B::display() {
	cout << "Class B: m_a=" << m_a << ", m_b=" << m_b << endl;
}

//基類C
class C {
public:
	C(int c);
public:
	void display();
protected:
	int m_c;
};
C::C(int c) : m_c(c) { }
void C::display() {
	cout << "Class C: m_c=" << m_c << endl;
}

//最終派生類D
class D : public B, public C {
public:
	D(int a, int b, int c, int d);
public:
	void display();
private:
	int m_d;
};
D::D(int a, int b, int c, int d) : B(a, b), C(c), m_d(d) { }
void D::display() {
	cout << "Class D: m_a=" << m_a << ", m_b=" << m_b << ", m_c=" << m_c << ", m_d=" << m_d << endl;
}


int main() {
	A *pa = new A(1);
	B *pb = new B(2, 20);
	C *pc = new C(3);
	D *pd = new D(4, 40, 400, 4000);

	cout << "-------更改前-----" << endl;
	pa->display();
	pb->display();
	pc->display();
	pd->display();
        cout << "-----------------------" << endl;

	cout << "--------更改後-----------" << endl;
    
	pa = pd;
	pa->display();

	pb = pd;
	pb->display();

	pc = pd;
	pc->display();

	pd->display();
	cout << "-----------------------" << endl;

	system("pause");
	return 0;
}

運行代碼:

        該例中我們定義了多個對象指針,並嘗試將派生類指針賦值給基類指針。與對象變量之間的賦值不同的是,對象指針之間的賦值並沒有拷貝對象的成員,也沒有修改對象本身的數據,僅僅是改變了指針的指向:

        在更改前,每個指針都指向對應類的對象,並且完成了對成員變量的賦值;

        接下來將派生類的指針pd依次賦給pa、pb、pc,由此可以發現:

        當我們將派生類指針 pd 賦值給基類指針 pa後,從運行結果可以看出,調用 display() 函數時雖然使用了派生類的成員變量,但是 display() 函數本身卻是基類的。也就是說,將派生類指針賦值給基類指針時,通過基類指針只能使用派生類的成員變量,但不能使用派生類的成員函數,pb、pc也是同樣的情況,這是爲什麼呢?

        a 本來是基類 A 的指針,現在指向了派生類 D 的對象,這使得隱式指針 this 發生了變化,也指向了 D 類的對象,所以最終在 display() 內部使用的是 D 類對象的成員變量,編譯器雖然通過指針的指向來訪問成員變量,但是卻不通過指針的指向來訪問成員函數:編譯器通過指針的類型來訪問成員函數。對於 pa,它的類型是 A,不管它指向哪個對象,使用的都是 A 類的成員函數,只不過該成員函數中使用的是D類對象的成員變量。

        總結一下:編譯器通過指針來訪問成員變量,指針指向哪個對象就使用哪個對象的數據;編譯器通過指針的類型來訪問成員函數,指針屬於哪個類的類型就使用哪個類的函數。


補充:

  • 通過基類的對象、指針、引用只能訪問從基類繼承過去的成員  (包括成員變量和成員函數),不能訪問派生類新增的成員。
  • 通過基類的引用或指針,調用基類/派生類的虛函數,要根據運行時根據指針或引用實際指向或引用的類型確定,調用非虛函數時,則無論基類指向的是何種類型,都調用基類的函數 。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章