C++ 向上转型
在 C++ 中經(jīng)常會(huì)發(fā)生數(shù)據(jù)類型的轉(zhuǎn)換,例如將 int 類型的數(shù)據(jù)賦值給 float 類型的變量時(shí),編譯器會(huì)先把 int 類型的數(shù)據(jù)轉(zhuǎn)換為 float 類型再賦值;反過(guò)來(lái),float 類型的數(shù)據(jù)在經(jīng)過(guò)類型轉(zhuǎn)換后也可以賦值給 int 類型的變量。
數(shù)據(jù)類型轉(zhuǎn)換的前提是,編譯器知道如何對(duì)數(shù)據(jù)進(jìn)行取舍。例如:
int a = 10.9; printf("%d\n", a);輸出結(jié)果為 10,編譯器會(huì)將小數(shù)部分直接丟掉(不是四舍五入)。再如:
float b = 10; printf("%f\n", b);輸出結(jié)果為 10.000000,編譯器會(huì)自動(dòng)添加小數(shù)部分。
類其實(shí)也是一種數(shù)據(jù)類型,也可以發(fā)生數(shù)據(jù)類型轉(zhuǎn)換,不過(guò)這種轉(zhuǎn)換只有在基類和派生類之間才有意義,并且只能將派生類賦值給基類,包括將派生類對(duì)象賦值給基類對(duì)象、將派生類指針賦值給基類指針、將派生類引用賦值給基類引用,這在 C++ 中稱為向上轉(zhuǎn)型(Upcasting)。相應(yīng)地,將基類賦值給派生類稱為向下轉(zhuǎn)型(Downcasting)。
向上轉(zhuǎn)型非常安全,可以由編譯器自動(dòng)完成;向下轉(zhuǎn)型有風(fēng)險(xiǎn),需要程序員手動(dòng)干預(yù)。
將派生類對(duì)象賦值給基類對(duì)象
下面的例子演示了如何將派生類對(duì)象賦值給基類對(duì)象:
#include <iostream> using namespace std;//基類 class A{ public:A(int a); public:void display(); public:int m_a; }; A::A(int a): m_a(a){ } void A::display(){cout<<"Class A: m_a="<<m_a<<endl; }//派生類 class B: public A{ public:B(int a, int b); public:void display(); public: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; }int main(){A a(10);B b(66, 99);//賦值前a.display();b.display();cout<<"--------------"<<endl;//賦值后a = b;a.display();b.display();return 0; }運(yùn)行結(jié)果:
Class A: m_a=10 Class B: m_a=66, m_b=99 ---------------------------- Class A: m_a=66 Class B: m_a=66, m_b=99本例中 A 是基類, B 是派生類,a、b 分別是它們的對(duì)象,由于派生類 B 包含了從基類 A 繼承來(lái)的成員,因此可以將派生類對(duì)象 b 賦值給基類對(duì)象 a。通過(guò)運(yùn)行結(jié)果也可以發(fā)現(xiàn),賦值后 a 所包含的成員變量的值已經(jīng)發(fā)生了變化。
賦值的本質(zhì)是將現(xiàn)有的數(shù)據(jù)寫入已分配好的內(nèi)存中,對(duì)象的內(nèi)存只包含了成員變量,所以對(duì)象之間的賦值是成員變量的賦值,成員函數(shù)不存在賦值問(wèn)題。
將派生類對(duì)象賦值給基類對(duì)象時(shí),會(huì)舍棄派生類新增的成員,也就是“大材小用”,如下圖所示:
可以發(fā)現(xiàn),即使將派生類對(duì)象賦值給基類對(duì)象,基類對(duì)象也不會(huì)包含派生類的成員,所以依然不同通過(guò)基類對(duì)象來(lái)訪問(wèn)派生類的成員。
這種轉(zhuǎn)換關(guān)系是不可逆的,只能用派生類對(duì)象給基類對(duì)象賦值,而不能用基類對(duì)象給派生類對(duì)象賦值。
將派生類指針賦值給基類指針
除了可以將派生類對(duì)象賦值給基類對(duì)象(對(duì)象變量之間的賦值),還可以將派生類指針賦值給基類指針(對(duì)象指針之間的賦值)。我們先來(lái)看一個(gè)多繼承的例子,繼承關(guān)系為:
下面的代碼實(shí)現(xiàn)了這種繼承關(guān)系:
運(yùn)行結(jié)果:
Class A: m_a=4 Class B: m_a=4, m_b=40 Class C: m_c=400 ----------------------- pa=0x9b17f8 pb=0x9b17f8 pc=0x9b1800 pd=0x9b17f8本例中定義了多個(gè)對(duì)象指針,并嘗試將派生類指針賦值給基類指針。與對(duì)象變量之間的賦值不同的是,對(duì)象指針之間的賦值并沒(méi)有拷貝對(duì)象的成員,也沒(méi)有修改對(duì)象本身的數(shù)據(jù),僅僅是改變了指針的指向。
將派生類引用賦值給基類引用
引用在本質(zhì)上是通過(guò)指針的方式實(shí)現(xiàn)的,既然基類的指針可以指向派生類的對(duì)象,那么我們就有理由推斷:基類的引用也可以指向派生類的對(duì)象,并且它的表現(xiàn)和指針是類似的。
修改上例中 main() 函數(shù)內(nèi)部的代碼,用引用取代指針:
int main(){D d(4, 40, 400, 4000);A &ra = d;B &rb = d;C &rc = d;ra.display();rb.display();rc.display();return 0; }運(yùn)行結(jié)果:
Class A: m_a=4 Class B: m_a=4, m_b=40 Class C: m_c=400ra、rb、rc 是基類的引用,它們都引用了派生類對(duì)象 d,并調(diào)用了 display() 函數(shù),從運(yùn)行結(jié)果可以發(fā)現(xiàn),雖然使用了派生類對(duì)象的成員變量,但是卻沒(méi)有使用派生類的成員函數(shù),這和指針的表現(xiàn)是一樣的。
引用和指針的表現(xiàn)之所以如此類似,是因?yàn)橐煤椭羔槻](méi)有本質(zhì)上的區(qū)別,引用僅僅是對(duì)指針進(jìn)行了簡(jiǎn)單封裝。
最后需要注意的是,向上轉(zhuǎn)型后通過(guò)基類的對(duì)象、指針、引用只能訪問(wèn)從基類繼承過(guò)去的成員(包括成員變量和成員函數(shù)),不能訪問(wèn)派生類新增的成員。
總結(jié)
- 上一篇: Java @Deprecated注解
- 下一篇: s3c2440移植MQTT