第5章 繼承和多態(tài)_第1頁
第5章 繼承和多態(tài)_第2頁
第5章 繼承和多態(tài)_第3頁
第5章 繼承和多態(tài)_第4頁
第5章 繼承和多態(tài)_第5頁
已閱讀5頁,還剩50頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

第5章繼承和多態(tài)本章主要內(nèi)容:5.1公有繼承和is-a關(guān)系

5.2派生類對象的構(gòu)造、析構(gòu)

5.3同名覆蓋原則

5.4賦值兼容原則

5.5多態(tài)性和虛函數(shù)

5.6典型范例——各類物體面積求和

5.7其它繼承方式

5.8多繼承

5.9深度探索 計(jì)算機(jī)學(xué)院李衛(wèi)明面向?qū)ο蟮暮诵乃枷胧浅橄?、封裝與信息隱蔽、繼承和多態(tài)。前面學(xué)習(xí)了對現(xiàn)實(shí)世界或思維世界中的實(shí)體進(jìn)行抽象,用類完成封裝,完成類的接口和類的實(shí)現(xiàn)的分離,類的用戶只需了解類的接口,無需關(guān)心類實(shí)現(xiàn)的細(xì)節(jié),達(dá)到了信息隱蔽目的,同時(shí),通過函數(shù)重載和運(yùn)算符重載實(shí)現(xiàn)了靜態(tài)多態(tài)性。本章學(xué)習(xí)面向?qū)ο蟪绦蛟O(shè)計(jì)中重要的繼承機(jī)制和通過動(dòng)態(tài)綁定實(shí)現(xiàn)動(dòng)態(tài)多態(tài)性。計(jì)算機(jī)學(xué)院李衛(wèi)明5.1 公有繼承和is-a關(guān)系

5.1.1 繼承和派生

人們對現(xiàn)實(shí)世界或思維世界中的實(shí)體進(jìn)行分類,形成樹形層次關(guān)系。如圖5.1所示,細(xì)分類的個(gè)體和基礎(chǔ)類個(gè)體之間具有特殊和一般的關(guān)系,細(xì)分類的個(gè)體也是一種基礎(chǔ)類的個(gè)體,稱為is-a關(guān)系,細(xì)分類個(gè)體除了具有基礎(chǔ)類個(gè)體的屬性和行為能力外,還具有自己特殊的屬性和行為能力。圖5.1分類關(guān)系舉例在面向?qū)ο蟪绦蛟O(shè)計(jì)中,用公有繼承和派生機(jī)制反映上述分類關(guān)系。用于描述基礎(chǔ)類個(gè)體的類稱為基類,也可稱為父類,用于描述細(xì)分類個(gè)體的類稱為派生類,也可稱為子類。計(jì)算機(jī)學(xué)院李衛(wèi)明

一個(gè)類可以直接派生出多個(gè)派生類,每個(gè)派生出的子類均繼承了基類的公有屬性和公有函數(shù)描述的功能,派生類本身也可作為基類繼續(xù)派生,形成多級派生。

一個(gè)類直接派生出的子類以及子類直接或間接派生出的所有類統(tǒng)稱為它的子孫類,一個(gè)類及它的子孫類形成一個(gè)派生關(guān)系類族,派生關(guān)系類族中從父類一直往上到最上層結(jié)點(diǎn)路徑上的所有類通稱為該類的祖先類,子孫類從祖先類直接或間接派生產(chǎn)生。子孫類繼承祖先類的所有公有屬性和公有函數(shù)成員描述的功能。計(jì)算機(jī)學(xué)院李衛(wèi)明聲明公有派生類的一般建議格式為:class派生類名:public基類名{public:派生類公有函數(shù)成員private:

派生類私有函數(shù)成員private:

派生類私有數(shù)據(jù)成員};與普通類聲明一樣,C++語言本身并未限制派生類成員的聲明順序。這里聲明的是公有派生,絕大部分派生是公有派生,反映is-a(是)關(guān)系,關(guān)于特殊場合使用的其它派生,請參見本章第7節(jié)。計(jì)算機(jī)學(xué)院李衛(wèi)明5.1.2 派生類定義C++中成員訪問控制修飾符有三種:private、protected、public,其中,私有成員只能在本類成員函數(shù)和友元函數(shù)里訪問,不可在派生類的成員函數(shù)里訪問,公有成員可以在任何地方訪問。protected訪問控制專用于繼承和派生,protected修飾的成員是保護(hù)成員,除了可以像私有成員在本類成員函數(shù)和友元函數(shù)里訪問外,還可在派生類的成員函數(shù)里訪問,但不可在類外訪問。

無論公有成員、私有成員還是保護(hù)成員,基類數(shù)據(jù)成員都是派生類對象的組成部分,公有繼承時(shí),基類的公有和保護(hù)成員可訪問性在派生類中保持不變,多級派生時(shí),子孫類成員函數(shù)也可以直接訪問祖先類的保護(hù)成員和公有成員。公有繼承時(shí),子孫類對象繼承了祖先類的公有數(shù)據(jù)屬性和公有函數(shù)功能,程序中任何位置可訪問對象祖先類的公有成員。下節(jié)樣例程序里我們可以看到,學(xué)生類CStudent和教師類CTeacher公有繼承普通人類CPerson,學(xué)生和教師個(gè)體作為派生類對象具有基類公有函數(shù)所描述的功能,同時(shí),派生類成員函數(shù)中可直接訪問基類中的保護(hù)成員m_strName。計(jì)算機(jī)學(xué)院李衛(wèi)明5.1.3 派生類訪問控制CPerson、CStudent、CTeacherUML圖示:

圖5.2普通人類Cperson、學(xué)生類Cstudent和教師類Cteacher具體樣例代碼見Ex5.1計(jì)算機(jī)學(xué)院李衛(wèi)明5.1.4 派生類樣例整個(gè)派生類對象狀態(tài)包含基類部分對象狀態(tài)和派生類部分?jǐn)?shù)據(jù)成員構(gòu)成的狀態(tài),派生類對象實(shí)際可分為兩部分:基類部分對象和派生類部分對象?;惒糠?jǐn)?shù)據(jù)成員,用于表示基類部分對象狀態(tài);派生類本身的所有數(shù)據(jù)成員,用于表示派生類部分對象狀態(tài)?;惒糠?jǐn)?shù)據(jù)成員包括直接基類的所有數(shù)據(jù)成員,也包括間接基類的所有數(shù)據(jù)成員,不論它們的訪問控制是公有、保護(hù),還是私有的。

派生類構(gòu)造函數(shù)一般形式如下:派生類名(形參表):基類構(gòu)造函數(shù)名(基類構(gòu)造函數(shù)參數(shù)表),數(shù)據(jù)成員1(數(shù)據(jù)成員參數(shù)表1),數(shù)據(jù)成員2(數(shù)據(jù)成員參數(shù)表2),…{……//函數(shù)體}對象建立時(shí)數(shù)據(jù)成員初始化由構(gòu)造函數(shù)完成,對象撤銷時(shí)掃尾處理由析構(gòu)函數(shù)完成?;悩?gòu)造函數(shù)只能完成基類部分對象的初始化,基類的析構(gòu)函數(shù)只能完成基類部分對象的掃尾處理,從這個(gè)意義來說,基類的構(gòu)造函數(shù)和析構(gòu)函數(shù)不可繼承。計(jì)算機(jī)學(xué)院李衛(wèi)明5.2 派生類對象的構(gòu)造、析構(gòu)派生類構(gòu)造函數(shù)會(huì)先調(diào)用基類構(gòu)造函數(shù)完成基類部分?jǐn)?shù)據(jù)成員的初始化,再完成派生類部分?jǐn)?shù)據(jù)成員的初始化,析構(gòu)時(shí),次序正好相反,派生類析構(gòu)函數(shù)會(huì)先執(zhí)行派生類析構(gòu)函數(shù)體,完成派生類部分對象的掃尾處理,再隱式調(diào)用基類析構(gòu)函數(shù)完成基類部分對象的掃尾處理。

樣例Ex5.1中基類、派生類均未定義構(gòu)造函數(shù)和析構(gòu)函數(shù),基類、派生類構(gòu)造函數(shù)和析構(gòu)函數(shù)由編譯器合成。編譯器合成的CPerson調(diào)用string類構(gòu)造函數(shù)完成名字成員m_strName初始化為空字符串,年齡成員m_iAge未初始化,派生類CStudent構(gòu)造函數(shù)先調(diào)用基類無參構(gòu)造函數(shù)完成基類部分對象的初始化,再完成數(shù)據(jù)成員子對象選修課表向量m_strVec初始化為空向量,派生類CTeacher構(gòu)造函數(shù)先調(diào)用基類無參構(gòu)造函數(shù)完成基類部分對象的初始化,再完成數(shù)據(jù)成員子對象講授課表向量m_strVec初始化為空向量。

計(jì)算機(jī)學(xué)院李衛(wèi)明如果需要在建立CStudent類對象時(shí)直接指定學(xué)生的名字和年齡,在建立CTeacher類對象時(shí)直接指定教師的名字和年齡,需要為基類CPerson類增加下述公有構(gòu)造函數(shù):

CPerson(stringstrName,intiAge):m_strName(strName),m_iAge(iAge){}

CStudent類和CTeacher類也需要增加下述公有構(gòu)造函數(shù):

CStudent(stringstrName,intiAge):CPerson(strName,iAge){}CTeacher(stringstrName,intiAge):CPerson(strName,iAge){}

計(jì)算機(jī)學(xué)院李衛(wèi)明上述派生類新增構(gòu)造函數(shù)在初始化列表里顯式調(diào)用基類的構(gòu)造函數(shù)。如果派生類構(gòu)造函數(shù)初始化列表里沒有顯式調(diào)用基類的構(gòu)造函數(shù),編譯器會(huì)隱式調(diào)用基類的缺省無參構(gòu)造函數(shù),當(dāng)基類有其它構(gòu)造函數(shù)而無缺省無參構(gòu)造函數(shù)時(shí),編譯器會(huì)報(bào)錯(cuò)。

計(jì)算機(jī)學(xué)院李衛(wèi)明一般情況下,派生類繼承了基類公有的數(shù)據(jù)成員和公有函數(shù)成員。如果派生類新增的數(shù)據(jù)成員或函數(shù)成員與基類中成員同名,則基類中所有同名成員名字被覆蓋,派生類成員函數(shù)中或外部不可通過派生類對象名直接訪問基類同名成員,這一原則稱為同名覆蓋原則。如我們?yōu)樯瞎?jié)樣例中CPerson類增加顯示人員信息的成員函數(shù)Show后:voidShow()const{cout<<m_strName<<","<<m_iAge<<"yearsold"<<endl;}再為CStudent類和CTeacher類增加顯示學(xué)生信息和教師信息的成員函數(shù)Show后,基類CPerson中同名成員函數(shù)已被覆蓋。CStudent類、CTeacher類的對象或它們的派生類對象調(diào)用的Show函數(shù)都是覆蓋后的派生類成員函數(shù)Show。

下面2個(gè)語句分別調(diào)用CStudent類、CTeacher類的成員函數(shù)Show:studZhang.Show();//studZhang是一個(gè)CStudent類對象teacherLiu.Show();//teacherLiu是一個(gè)CTeacher類對象計(jì)算機(jī)學(xué)院李衛(wèi)明5.3 同名覆蓋原則如果需要調(diào)用基類中被覆蓋的同名成員函數(shù),調(diào)用時(shí)可在成員函數(shù)名前添加基類名::。如下述CStudent類和CTeacher類成員函數(shù)Show實(shí)現(xiàn)時(shí),用CPerson::Show()調(diào)用基類Show成員函數(shù):voidCStudent::Show()const{CPerson::Show();cout<<"isastudent"<<endl;}voidCTeacher::Show()const{CPerson::Show();cout<<"isateacher"<<endl;}同名覆蓋后,CStudent類和CTeacher類對象,可用下述方式調(diào)用基類Show成員函數(shù):studZhang.CPerson::Show();//studZhang是一個(gè)CStudent類對象teacherLiu.CPerson::Show();//teacherLiu是一個(gè)CTeacher類對象計(jì)算機(jī)學(xué)院李衛(wèi)明公有繼承下,派生類或子孫類對象也是一種特殊的基類對象,具有基類對象的屬性和公有函數(shù)描述的功能,因此,在需要基類對象的場合,派生類對象或子孫類對象也都可以滿足需要,這稱為賦值兼容原則。具體如下:首先,考慮對象指針類型。指針類型變量用于存放對象的地址,指向?qū)ο?。指針類型變量可以指向本類對象,也允許基類指針變量指向派生類對象或子孫類對象,但不可以指向祖先類對象;基類指針變量作為形參時(shí),實(shí)參可以是指向本類對象的指針、指向派生類對象或子孫類對象的指針,不可以是指向祖先類對象的指針;函數(shù)返回對象指針類型時(shí),函數(shù)可以實(shí)際返回本類對象的指針、派生類對象或子孫類對象的指針,不可以返回祖先類對象的指針。賦值時(shí),可以給本類對象指針類型的變量賦值本類對象指針、派生類對象指針或子孫類對象指針,不可以將祖先類對象的指針賦值給基類類型的指針變量。計(jì)算機(jī)學(xué)院李衛(wèi)明5.4 賦值兼容原則函數(shù)OlderOne返回候選者中選出年齡較大者的指針:CPerson*OlderOne(CPserson*first,CPserson*second);可以調(diào)用函數(shù)OlderOne從兩位學(xué)生中選出年齡大的一位,也可以調(diào)用函數(shù)OlderOne從學(xué)生和教師中選出年齡大的一位:CPerson*pOlderOne;pOlderOne=OlderOne(&studZhang,&studWang);CPerson*pOlderPerson=OlderOne(&zhang,&teacherLiu);但不可以將基類指針變量賦值給派生類指針變量,如:CStudent*pOlderStudent=pOlderOne;//錯(cuò)誤函數(shù)BestStudent可以從兩位學(xué)生中選出選課多的人,返回學(xué)生指針:CStudent*BestStudent(CStudent*first,CStudent*second);可以調(diào)用函數(shù)BestStudent從兩位學(xué)生中選出選課多的一位:CStudent*pBestStudent=BestStudent(&studZhang,&studWang);但不可以調(diào)用函數(shù)BestStudent從學(xué)生和教師中選出選課多的一位,也不可以從普通人和學(xué)生中選出選課多的一位:BestStudent(&studZhang,&teacherLiu);//錯(cuò)誤BestStudent(&studZhang,&someOne);//錯(cuò)誤計(jì)算機(jī)學(xué)院李衛(wèi)明我們再來考慮引用。引用具有指針類似道理。引用是對象的別名,引用對象與被引用對象是同一個(gè)對象。引用變量可以引用本類對象,C++允許基類引用變量引用派生類對象或子孫類對象,但不可以引用祖先類對象;基類引用變量作為形參時(shí),實(shí)參可以是本類對象、派生類對象或子孫類對象,實(shí)參不可以是祖先類對象;函數(shù)返回對象引用時(shí),函數(shù)可以實(shí)際返回本類對象、派生類對象或子孫類對象,但不可以返回祖先類對象。定義引用變量初始化時(shí),可以初始化為本類對象、派生類對象或子孫類對象,不可以初始化為祖先類對象。計(jì)算機(jī)學(xué)院李衛(wèi)明函數(shù)OlderOne2返回候選者中年齡最大者的引用,原型如下:constCPerson&OlderOne2(constCPserson&first,constCPserson&second);可以調(diào)用函數(shù)OlderOne2從兩位學(xué)生中選出年齡大的一位,也可以從學(xué)生和教師中選出年齡大的一位:CPerson&olderOne=OlderOne2(studZhang,studWang);CPerson&olderPerson=OlderOne2(studZhang,teacherLiu);不可以定義派生類引用變量時(shí)將其初始化為基類對象:CStudent&olderStudent=olderOne;//錯(cuò)誤函數(shù)BestStudent返回選課多學(xué)生的引用:constCStudent&BestStudent2(constCStudent&first,constCStudent&second);可以調(diào)用函數(shù)BestStudent從兩位學(xué)生中選出選課多的一位:constCStudent&bestStudent=BestStudent2(studZhang,studWang);不可以調(diào)用函數(shù)BestStudent2從學(xué)生和教師中選出選課多的一位,也不可以從普通人和學(xué)生中選出選課多的一位:BestStudent2(studZhang,teacherLiu);//錯(cuò)誤BestStudent2(studZhang,someOne);//錯(cuò)誤,someOne不是學(xué)生對象計(jì)算機(jī)學(xué)院李衛(wèi)明考慮類類型普通變量和函數(shù)參數(shù)傳值、函數(shù)返回普通對象類型情況。一個(gè)類類型變量,可以存放一個(gè)同類對象,不能存放其它類類型對象。C++可以將對象賦值給同類對象變量,同類對象賦值時(shí)實(shí)際執(zhí)行的是對象的復(fù)制賦值或轉(zhuǎn)移賦值。C++不可以將對象賦值給不相關(guān)類對象變量或派生類對象變量。C++允許將對象賦值給基類對象變量,不會(huì)編譯報(bào)錯(cuò)、甚至不會(huì)警告,但實(shí)際復(fù)制的只是基類部分子對象,不是派生類對象的全部,這個(gè)現(xiàn)象稱為切片現(xiàn)象,一般場合下存在切片現(xiàn)象的賦值是不合適的。參數(shù)采用傳值方式時(shí),基類變量作為形參,實(shí)參可以是本類對象、不可以是祖先類對象,C++允許派生類對象或子孫類對象作實(shí)參,但復(fù)制給形參的只是基類部分對象,也存在切片現(xiàn)象,并不合適;

函數(shù)返回對象時(shí),函數(shù)可以實(shí)際返回本類對象,不可以返回祖先類對象,C++允許返回派生類對象或子孫類對象,但返回的只是基類部分對象,存在切片現(xiàn)象,也并不合適。此外,大對象在函數(shù)間傳遞時(shí),如果參數(shù)采用傳值方式,存在對象的復(fù)制,效率會(huì)較低,一般應(yīng)該盡量避免大對象的傳值方式傳遞或傳值方式返回,同時(shí),要保證不可返回臨時(shí)對象的引用或指針,因?yàn)榕R時(shí)對象在函數(shù)返回后已析構(gòu)。計(jì)算機(jī)學(xué)院李衛(wèi)明面向?qū)ο蟪绦蛟O(shè)計(jì)中,作用在對象上的函數(shù)或運(yùn)算符的調(diào)用可以看作向?qū)ο蟀l(fā)出某個(gè)消息,對象接收消息后做出某些動(dòng)作。不同類的對象接收同一個(gè)消息后可以調(diào)用不同的函數(shù)體,做出不同的動(dòng)作,這個(gè)現(xiàn)象稱為多態(tài)性。抽象、封裝、繼承和多態(tài)性是面向?qū)ο蟪绦蛟O(shè)計(jì)的主要特征。C++面向?qū)ο蟪绦蛟O(shè)計(jì)中多態(tài)性可根據(jù)決定調(diào)用函數(shù)時(shí)機(jī)分為靜態(tài)多態(tài)性和動(dòng)態(tài)動(dòng)態(tài)性。如果在編譯期間確定實(shí)際調(diào)用的函數(shù),也就是采用靜態(tài)綁定(binding),這樣的多態(tài)性稱為靜態(tài)多態(tài)性;如果編譯期間不能確定實(shí)際調(diào)用的函數(shù),需要程序執(zhí)行到具體函數(shù)調(diào)用語句時(shí)才能確定實(shí)際調(diào)用的函數(shù),也就是需要采用動(dòng)態(tài)綁定(binding),這樣的多態(tài)性稱為動(dòng)態(tài)多態(tài)性。前面介紹的普通函數(shù)重載和成員函數(shù)重載都是在編譯階段確定實(shí)際調(diào)用的函數(shù),因此,函數(shù)重載是一種靜態(tài)多態(tài)性。下一章介紹的函數(shù)模板和類模板也是在編譯階段確定實(shí)際調(diào)用的函數(shù)和實(shí)際使用的類及相應(yīng)成員函數(shù),這樣的多態(tài)性也是一種靜態(tài)多態(tài)性。動(dòng)態(tài)多態(tài)性是一種面向?qū)ο蟪绦蛟O(shè)計(jì)中非常重要的多態(tài)性。例如:同樣調(diào)用run方法,飛鳥調(diào)用時(shí)動(dòng)作是飛,馬調(diào)用時(shí)動(dòng)作是奔跑。計(jì)算機(jī)學(xué)院李衛(wèi)明5.5 多態(tài)性和虛函數(shù)5.5.1 多態(tài)性面向?qū)ο蟪绦蛟O(shè)計(jì)中動(dòng)態(tài)多態(tài)性的實(shí)現(xiàn)受到繼承性的支持,通過在派生類中重定義基類虛函數(shù)來實(shí)現(xiàn)多態(tài)性,利用類繼承的層次關(guān)系,把具有通用功能的協(xié)議存放在類層次中盡可能高的地方,而將實(shí)現(xiàn)這一功能的不同方法置于較低層次,在這些低層次上生成的對象就能給通用消息以不同的響應(yīng)。同一類族的對象可以統(tǒng)一管理,利用多態(tài)性用戶可發(fā)送一個(gè)通用的信息給同一類族的對象,而將所有的實(shí)現(xiàn)細(xì)節(jié)都留給接收消息的對象自行決定,不同類型的對象即可調(diào)用不同的函數(shù)響應(yīng)同一消息,簡化了程序。計(jì)算機(jī)學(xué)院李衛(wèi)明樣例Ex5.2在Ex51基礎(chǔ)上,綜合了前面各節(jié)情況,各類增加了構(gòu)造函數(shù)、增加了成員函數(shù)Show,增加了三個(gè)普通函數(shù)ShowByPointer、ShowByReference、ShowByValue,參數(shù)分別以對象指針、對象引用、對象傳值方式傳遞,根據(jù)賦值兼容原則,調(diào)用這三個(gè)函數(shù)時(shí)形參實(shí)際是派生類對象指針(語句S112~S114)、派生類對象引用(語句S117~S119)、派生類對象的切片(語句S122~S124)。從運(yùn)行結(jié)果我們看到,無論何種情況,樣例程序調(diào)用的都是基類的Show函數(shù),我們需要的動(dòng)態(tài)多態(tài)性并未發(fā)生。由于C++采用的是靜態(tài)綁定,這些情況下,普通函數(shù)ShowByPointer、ShowByReference、ShowByValue三個(gè)函數(shù)中參數(shù)類型分別為基類對象指針、基類對象引用、基類對象,綁定的都是基類的Show成員函數(shù)。

計(jì)算機(jī)學(xué)院李衛(wèi)明5.5.2 虛函數(shù)C++動(dòng)態(tài)多態(tài)性如何實(shí)現(xiàn)呢?如果需要?jiǎng)討B(tài)多態(tài)性,我們需要在基類成員函數(shù)聲明或定義前加入關(guān)鍵字virtual,聲明這個(gè)成員函數(shù)是虛函數(shù),也就是上述樣例語句S13函數(shù)定義前加關(guān)鍵字virtual,聲明Show成員函數(shù)是虛函數(shù),派生類成員函數(shù)Show前是否加關(guān)鍵字virtual沒有實(shí)際影響,C++對虛函數(shù)采用動(dòng)態(tài)綁定。修改后運(yùn)行結(jié)果中,通過對象指針和引用調(diào)用虛函數(shù)都體現(xiàn)出動(dòng)態(tài)多態(tài)性,通過傳值調(diào)用時(shí),形參得到的實(shí)際是派生類對象的切片,已完全是基類對象,因此無動(dòng)態(tài)多態(tài)性。C++規(guī)定構(gòu)造函數(shù)不可以是虛函數(shù),析構(gòu)函數(shù)可以是虛函數(shù)。類成員函數(shù)中直接調(diào)用虛函數(shù)或通過this調(diào)用虛函數(shù)都是當(dāng)前實(shí)際對象類型自己定義或繼承的虛函數(shù),具有動(dòng)態(tài)多態(tài)性。構(gòu)造函數(shù)里調(diào)用的虛函數(shù)時(shí),由于對象派生類部分尚未構(gòu)造完成,調(diào)用的是構(gòu)造函數(shù)所在類或繼承的虛函數(shù),不是派生類中定義的虛函數(shù)。計(jì)算機(jī)學(xué)院李衛(wèi)明

C++規(guī)定構(gòu)造函數(shù)不可以是虛函數(shù),析構(gòu)函數(shù)可以是虛函數(shù)。那么,如何精確復(fù)制一個(gè)基類指針?biāo)傅呐缮悓ο竽??例如,我們有哺乳?dòng)物類CMammal,從哺乳動(dòng)物類CMammal派生出狗類CDog和馬類CHorse,指針向量zoo定義如下:vector<CMammal*>zoo;程序運(yùn)行過程中,指針向量zoo已存放若干動(dòng)物對象指針,每個(gè)指針分別指向狗類CDog對象或馬類CHorse對象,如何復(fù)制出原指針向量zoo里管理的動(dòng)物,讓動(dòng)物加倍呢?我們可以采用如下克隆技術(shù),就是在哺乳動(dòng)物類CMammal里增加純虛函數(shù)(在下節(jié)介紹)克隆Clone,在狗類CDog和馬類具體實(shí)現(xiàn)Clone函數(shù):計(jì)算機(jī)學(xué)院李衛(wèi)明5.5.3 派生類對象的克隆classCMammal{public:virtualCMammal*Clone()const=0;...//其它部分省略};classCDog:publicCMammal{public:CMammal*Clone()const{returnnewCDog(*this);//調(diào)用拷貝構(gòu)造動(dòng)態(tài)生成一只狗}...//其它部分省略};計(jì)算機(jī)學(xué)院李衛(wèi)明classCHorse:publicCMammal{public:CMammal*Clone()const{returnnewCHorse(*this);//調(diào)用拷貝構(gòu)造動(dòng)態(tài)生成一匹馬}...//其它部分省略};下列代碼執(zhí)行后將zoo中原有動(dòng)物逐個(gè)克隆,zoo中動(dòng)物個(gè)數(shù)多了一倍。

size_tn=zoo.size();//原動(dòng)物數(shù)量

for(size_ti=0;i<n;++i){zoo.push_back(zoo[i]->Clone());//克隆原動(dòng)物,放入zoo}計(jì)算機(jī)學(xué)院李衛(wèi)明考慮類族統(tǒng)一接口的需要,有時(shí)在基類中將某一成員函數(shù)聲明為虛函數(shù),只是在基類中預(yù)留了一個(gè)接口函數(shù)名,具體功能在基類中并無實(shí)現(xiàn),而是在派生類中根據(jù)需要去實(shí)現(xiàn),正如上節(jié)動(dòng)物園例子中所述。這時(shí)應(yīng)當(dāng)將基類的這個(gè)成員函數(shù)聲明為純虛函數(shù),聲明純虛函數(shù)的一般形式是:virtual返回值類型函數(shù)名(形參表)=0;純虛函數(shù)沒有函數(shù)定義,不能被調(diào)用,包含純虛函數(shù)的類稱為抽象類或抽象基類。抽象類無法建立對象,只作為一種用作繼承時(shí)的基類。如果抽象類派生出的新類還有未定義的純虛函數(shù),新派生類仍然是抽象類。只有所派生出的新類中已定義了基類的所有純虛函數(shù),這時(shí)所有虛函數(shù)就可以被調(diào)用,這樣的派生類就不再是抽象類,可以用來建立具體的對象。雖然不能建立抽象類的對象,但可以定義抽象類的指針變量,用指針變量指向派生類對象,然后通過該指針調(diào)用虛函數(shù),實(shí)現(xiàn)多態(tài)性的操作。下節(jié)講述使用了抽象類的典型案例。計(jì)算機(jī)學(xué)院李衛(wèi)明5.5.4 純虛函數(shù)和抽象類程序設(shè)計(jì)中經(jīng)常需要求各類形狀物體的面積。樣例Ex5.3定義了抽象形狀基類CShape,具有求面積的純虛函數(shù)GetArea,由CShape類派生出3個(gè)類:圓類CCircle、長方形類CRectangle和三角形類CTriangle。程序中定義了求面積和函數(shù)doubleTotalArea(constvector<CShape*>&),該函數(shù)用虛函數(shù)計(jì)算向量里各基類指針?biāo)肝矬w的面積并求和。樣例程序動(dòng)態(tài)生成各類對象,將對象指針保存在指針向量容器里,需要時(shí)可以調(diào)用求面積函數(shù)求容器里指針指向各類對象的正確面積,實(shí)現(xiàn)了動(dòng)態(tài)多態(tài)性,達(dá)到了對象創(chuàng)建和對象使用的分離,即使將來需要增加新類型,如橢圓形,TotalArea函數(shù)也無需修改。程序執(zhí)行結(jié)束時(shí),指針向量容器vecShapes正常析構(gòu),容器對象析構(gòu)時(shí)先執(zhí)行容器里每個(gè)元素對象的析構(gòu),再釋放本容器直接動(dòng)態(tài)分配的內(nèi)存空間。本例中容器里元素類型為普通指針,析構(gòu)時(shí)并不會(huì)主動(dòng)刪除所指對象,需要顯式遍歷刪除所指對象,由于基類析構(gòu)函數(shù)是虛函數(shù),所以可以動(dòng)態(tài)調(diào)用各對象相應(yīng)類的析構(gòu)函數(shù),完成資源的釋放。如果配合第7章智能指針,本例中各對象的刪除可以自動(dòng)完成。具體代碼參見樣例。計(jì)算機(jī)學(xué)院李衛(wèi)明5.6 典型范例——各類物體面積求和前面討論都是公有繼承,反映派生類對象也是一種基類對象,繼承了基類對象的特征。除了公有繼承,C++允許私有繼承和保護(hù)繼承,語法上只需將公有繼承關(guān)鍵字改為private或protected即可,形式如下:class派生類名:private基類名{...};class派生類名:protected基類名{...};私有繼承后,基類公有成員和保護(hù)成員在派生類里成為私有成員,也就是派生類對象不再具有基類對象所具有的功能,派生類對象不再是一種基類對象,不符合賦值兼容原則。保護(hù)繼承后,基類公有成員和保護(hù)成員在派生類里成為保護(hù)成員,派生類對象不再具有基類對象所具有的功能,派生類對象也不再是一種基類對象,也不符合賦值兼容原則。計(jì)算機(jī)學(xué)院李衛(wèi)明5.7 其它繼承方式程序設(shè)計(jì)中,絕大部分繼承是公有繼承,私有繼承和保護(hù)繼承很少使用。C++規(guī)定,沒有聲明繼承方式時(shí),默認(rèn)繼承方式是私有繼承。

Ex5.4是私有繼承的樣例,它通過私有繼承整形雙鏈表類list<int>實(shí)現(xiàn)了棧類,利用雙鏈表類功能很方便地實(shí)現(xiàn)了棧類功能。注意,棧類對象不再是一個(gè)雙鏈表對象,不具有雙鏈表對象具有的功能。

具體代碼參見樣例EX5.4。計(jì)算機(jī)學(xué)院李衛(wèi)明在第2章中,我們知道類的數(shù)據(jù)成員可以是其它類類型的對象,具有其它類類型,這個(gè)類類型和數(shù)據(jù)成員的類類型之間形成一種組合關(guān)系,數(shù)據(jù)成員對象是類類型主對象的一部分,也可以稱為子對象,主對象和子對象具有整體和局部的關(guān)系,是一種有(has-a)關(guān)系,主對象功能可以通過子對象功能實(shí)現(xiàn)。本章討論的公有繼承關(guān)系,反映一種是(is-a)關(guān)系,派生類對象也是一種基類對象,具有基類對象的功能,派生類對象也具有基類對象部分。所以,反映是(is-a)關(guān)系時(shí)采用公有繼承,反映有(has-a)關(guān)系時(shí)采用組合。私有繼承、保護(hù)繼承和組合間建議優(yōu)先采用組合。計(jì)算機(jī)學(xué)院李衛(wèi)明5.7.2 繼承與組合如果一個(gè)派生類有兩個(gè)或更多個(gè)基類,這種行為稱為多繼承。C++允許多繼承,具有多繼承的派生類的一般聲明格式如下:class派生類名:繼承方式1基類名1,繼承方式2基類名2……{……//派生類新增的數(shù)據(jù)成員和成員函數(shù)};例如已聲明了類A和類B,可按如下方式聲明多繼承的派生類C:classC:publicA,privateB{……//類C新增的數(shù)據(jù)成員和成員函數(shù)}派生類對象除具有派生類里描述的公有屬性和公有函數(shù)描述的功能外,還繼承了公有繼承的各基類的公有屬性和公有函數(shù)功能。例如,鳥類具有飛功能,馬類具有奔跑的功能,飛馬類公有繼承了鳥類和馬類,飛馬類對象既具有飛的功能,又具有奔跑的功能。

計(jì)算機(jī)學(xué)院李衛(wèi)明5.8 多繼承繼承時(shí),派生類對象包含各基類部分對象和派生類數(shù)據(jù)成員子對象。建立派生類對象時(shí),各基類部分對象和數(shù)據(jù)成員子對象的初始化通過派生類構(gòu)造函數(shù)完成。多繼承派生類的構(gòu)造函數(shù)在參數(shù)初始化表中可包含多個(gè)基類構(gòu)造函數(shù),一般定義格式如下:派生類名(形參表):基類名1(基類構(gòu)造函數(shù)參數(shù)表1),基類名2(基類構(gòu)造函數(shù)參數(shù)表2),…,數(shù)據(jù)成員1(數(shù)據(jù)成員參數(shù)表),數(shù)據(jù)成員2(數(shù)據(jù)成員參數(shù)表2),…{……//函數(shù)體}構(gòu)造函數(shù)中各基類構(gòu)造順序應(yīng)該與聲明派生類時(shí)繼承順序一致,派生類構(gòu)造函數(shù)的執(zhí)行順序?yàn)椋合日{(diào)用各基類的構(gòu)造函數(shù),再執(zhí)行各數(shù)據(jù)成員的構(gòu)造或初始化,最后執(zhí)行派生類構(gòu)造函數(shù)的函數(shù)體。派生類對象撤銷時(shí)會(huì)執(zhí)行派生類析構(gòu)函數(shù),完成數(shù)據(jù)成員子對象和基類部分對象的掃尾處理。析構(gòu)函數(shù)的執(zhí)行順序與構(gòu)造函數(shù)的執(zhí)行順序正好相反。多繼承在實(shí)際應(yīng)用中并不普遍。計(jì)算機(jī)學(xué)院李衛(wèi)明多繼承在帶來便利性的同時(shí),也可能會(huì)帶來一些問題。其中一個(gè)問題是二義性問題,比如,兩個(gè)類都具有同名的公有成員函數(shù),派生類對象從兩個(gè)類都繼承了這功能,調(diào)用時(shí)就會(huì)有二義性。解決二義性的辦法是通過派生類對象調(diào)用時(shí)在函數(shù)名前加類名::指定調(diào)用某個(gè)類的成員函數(shù),如樣例Ex5.5所示,或在派生類中定義同名成員函數(shù)覆蓋基類同名成員函數(shù),在派生類同名成員函數(shù)里通過類名::指明調(diào)用哪個(gè)基類成員函數(shù)。

具體參見樣例代碼Ex5.5。計(jì)算機(jī)學(xué)院李衛(wèi)明5.8.2 二義性問題解決辦法多繼承可能帶來的另一個(gè)問題是信息冗余和不一致問題。比如,兩個(gè)類從同一個(gè)類派生,而這兩個(gè)類又共同派生出了一個(gè)新類,形成了一個(gè)菱形的繼承關(guān)系。B類和C類都從A類派生,而D類通過多繼承由B與C共同派生。如果類A有成員函數(shù)Show(),通過D的對象去訪問A類的成員函數(shù)Show(),這時(shí)由于類B和類C都有繼承來自于類A的Show()的副本,編譯器無法確定使用哪個(gè)副本,將報(bào)錯(cuò)。同時(shí),D的對象中存在2份A類對象部分,即A類數(shù)據(jù)成員有2份,造成信息冗余和信息不一致問題。計(jì)算機(jī)學(xué)院李衛(wèi)明5.8.3 *虛繼承虛繼承是解決多義性問題的一種簡便而有效的方法。虛繼承由關(guān)鍵字virtual標(biāo)識(shí),一般語法格式如下:class派生類名:virtual繼承方式基類名虛繼承時(shí)的基類稱為虛基類。虛基類不是在基類定義時(shí)聲明,而是在聲明派生類時(shí),在繼承方式前加關(guān)鍵字vitual聲明。虛基類的最終子孫類對象只建立一份虛基類部分對象,虛基類部分對象構(gòu)造方法在最終建立對象的子孫類構(gòu)造函數(shù)中指明,忽略中間類構(gòu)造函數(shù)里虛基類構(gòu)造部分,中間類部分對象構(gòu)造時(shí)將不再構(gòu)造虛基類部分對象。這樣將不會(huì)導(dǎo)致信息冗余和不一致問題,也不會(huì)出現(xiàn)多義性問題。如果在虛基類中定義有帶參數(shù)的構(gòu)造函數(shù),并且參數(shù)沒有默認(rèn)值,而且沒有定義無參構(gòu)造函數(shù),則在虛基類的直接派生類或間接派生類的構(gòu)造函數(shù)的初始化表中都要對虛基類進(jìn)行初始化,均應(yīng)加上:虛基類構(gòu)造函數(shù)名(參數(shù)表)計(jì)算機(jī)學(xué)院李衛(wèi)明樣例Ex5.6中描述了普通人類CPerson、研究生類CGraduate、教師類CTeacher、在職研究生CGraduateTeacher類,各類之間的繼承關(guān)系如圖5.3所示。CPerson具有姓名、年齡數(shù)據(jù)成員和分別設(shè)置、獲取姓名、年齡的公有函數(shù)成員,CGraduate類具有學(xué)習(xí)某課程能力和顯示已學(xué)課程列表功能,CTeacher類具有教授某課程和顯示所有教授課程能力,CGraduate類和CTeacher類繼承了CPerson類公有成員。CGraduateTeacher類共有繼承CGraduate類、CTeacher類,CGraduateTeacher類對象研究生助教可以認(rèn)為既是研究生,也是教師,具有研究生和教師的雙重功能。具體代碼參見Ex5.6。圖5.3研究生助教類CGraduateTeacher菱形繼承關(guān)系圖計(jì)算機(jī)學(xué)院李衛(wèi)明前面講述了利用繼承和多態(tài)性進(jìn)行C++程序設(shè)計(jì)。本節(jié)深度探索派生類對象的內(nèi)存分布、虛函數(shù)機(jī)制實(shí)現(xiàn)原理和特殊場合使用的運(yùn)行時(shí)類型識(shí)別和動(dòng)態(tài)類型轉(zhuǎn)換。C++標(biāo)準(zhǔn)并未規(guī)定這些機(jī)制如何實(shí)現(xiàn),探索這些機(jī)制的實(shí)現(xiàn)原理,有利于加深對C++程序執(zhí)行機(jī)制的理解,設(shè)計(jì)出結(jié)構(gòu)良好、高效的程序。計(jì)算機(jī)學(xué)院李衛(wèi)明5.9 深度探索無論是公有繼承、保護(hù)繼承還是私有繼承,派生類對象內(nèi)部都包含了基類部分對象的數(shù)據(jù)成員和派生類新增數(shù)據(jù)成員,基類部分對象的數(shù)據(jù)成員本身也可能由祖先類部分對象的數(shù)據(jù)成員和基類本身數(shù)據(jù)成員組成。根據(jù)賦值兼容原則,基類指針可以指向派生類對象。下面探討派生類對象的內(nèi)存分布。計(jì)算機(jī)學(xué)院李衛(wèi)明5.9.1 派生類對象的內(nèi)存分布//Ex5.7A完整代碼見樣例classCBase{//成員省略};classCDerived:publicCbase{//函數(shù)省略};intmain(){CDerivedd;CBase*pb;CDerived*pd;pd=&d;pb=pd;cout<<reinterpret_cast<int>(pd)<<endl;//輸出指針值

cout<<reinterpret_cast<int>(pb)<<endl;}計(jì)算機(jī)學(xué)院李衛(wèi)明5.9.1.1 單繼承派生類對象內(nèi)存分布如圖5.4所示,樣例Ex5.7A派生類對象d內(nèi)存包含基類部分對象的數(shù)據(jù)成員和派生類部分新增的數(shù)據(jù)成員,派生類指針變量pd指向派生類對象d,根據(jù)賦值兼容原則,可以將派生類對象指針pd賦值給基類對象指針pb,單繼承時(shí),2個(gè)指針變量值相同。圖5.4單繼承CDerived對象內(nèi)存分布圖計(jì)算機(jī)學(xué)院李衛(wèi)明//Ex5.7B完整代碼見樣例classCBase1{//成員省略};classCBase2{//成員省略};classCDerived:publicCBase1,publicCBase2{//成員省略};intmain(){CDerivedd;CBase1*pb1;CBase2*pb2;CDerived*pd;pd=&d;pb1=pd;pb2=pd;cout<<reinterpret_cast<int>(pd)<<endl;//輸出指針值

cout<<reinterpret_cast<int>(pb1)<<endl;cout<<reinterpret_cast<int>(pb2)<<endl;}計(jì)算機(jī)學(xué)院李衛(wèi)明5.9.1.2 *多繼承派生類對象內(nèi)存分布如圖5.5所示,派生類對象d內(nèi)存包含2個(gè)基類部分對象的數(shù)據(jù)成員和派生類部分新增的數(shù)據(jù)成員,派生類指針變量pd指向派生類對象d,根據(jù)賦值兼容原則,可以將派生類對象指針pd賦值給基類對象指針pb1和基類對象指針pb2,多繼承時(shí),pb1、pd指針變量值相同,pb2指針變量值不同,比pb1、pd指針變量值大CBase1部分對象內(nèi)存大?。?字節(jié)。這里,我們看到指針變量賦值不僅僅是數(shù)值的復(fù)制。注意,當(dāng)派生類對象指針變量pd值為nullptr時(shí),基類對象指針pb1和基類對象指針pb2值應(yīng)該也是nullptr。圖5.5多繼承CDerived對象內(nèi)存分布圖計(jì)算機(jī)學(xué)院李衛(wèi)明//Ex5.7C完整代碼見樣例classCCommonBase{//成員省略};classCBase1:virtualpublicCCommonBase{//成員省略};classCBase2:virtualpublicCCommonBase{//成員省略};classCDerived:publicCBase1,publicCBase2{//成員省略};intmain(){CDerivedd;CCommonBase*pcb;CBase1*pb1;CBase2*pb2;CDerived*pd;pd=&d;pb1=pd;pb2=pd;pcb=pd;cout<<reinterpret_cast<int>(pd)<<endl;//輸出指針值

cout<<reinterpret_cast<int>(pcb)<<endl;cout<<reinterpret_cast<int>(pb1)<<endl;cout<<reinterpret_cast<int>(pb2)<<endl;}

計(jì)算機(jī)學(xué)院李衛(wèi)明5.9.1.3 *虛繼承派生類對象內(nèi)存分布如圖5.6所示,派生類對象d內(nèi)存包含1份CCommonBase部分對象、2個(gè)基類CBase1、CBase2部分對象和派生類部分新增的數(shù)據(jù)成員。pd指向派生類對象d,根據(jù)賦值兼容原則,可以將pd賦值給pb1、pb2、pcb,pb1、pb2也需要能夠訪問CCommonBase部分對象,CBase1、CBase2基類部分對象包含公共基類部分對象的偏移或地址,pb1、pd指針變量值相同,pb2指針變量值不同,比pb1、pd指針變量值大CBase1部分對象內(nèi)存大?。簲?shù)據(jù)成員和偏移量共8字節(jié),pcb指針變量值,比pb1、pd指針變量值大CBase1部分對象內(nèi)存大小8字節(jié)、CBase2部分對象內(nèi)存大小8字節(jié)和CDerived類新增數(shù)據(jù)成員大小4字節(jié):共20字節(jié)。這里展示了指針變量賦值不僅僅是數(shù)值復(fù)制的更復(fù)雜案例。注意,當(dāng)pd值為nullptr時(shí),pb1、pb2、pcb值應(yīng)該也是nullptr。圖5.6虛繼承CDerived對象內(nèi)存分布圖計(jì)算機(jī)學(xué)院李衛(wèi)明C++中動(dòng)態(tài)多態(tài)性通過虛函數(shù)體現(xiàn),編譯器一般是通過為每個(gè)具有虛函數(shù)的類建立虛函數(shù)表實(shí)現(xiàn)虛函數(shù)的。計(jì)算機(jī)學(xué)院李衛(wèi)明5.9.2 虛函數(shù)實(shí)現(xiàn)原理虛函數(shù)體現(xiàn)了多態(tài)性.classCDog:classCAnimal{…};CAnimal*p=newCDog;p->Move();虛函數(shù)工作原理.V-表(VirtualTable)中包含虛函數(shù)地址.基類數(shù)據(jù)成員派生類數(shù)據(jù)成員對象內(nèi)存分布圖V-表如圖5.7所示,基類CBase和派生類CDerived都具有虛函數(shù)。CBase類虛函數(shù)表包含2個(gè)指針項(xiàng),指向CBase類f1、f2函數(shù),CDerived類虛函數(shù)表包含3個(gè)指針項(xiàng),指向CDerived類改寫后的f1函數(shù)、繼承CBase類的f2函數(shù)、CDerived類新增的f3函數(shù),每個(gè)具有含虛函數(shù)類的對象都含有一個(gè)指針vptr,指向各自類的虛函數(shù)表,調(diào)用虛函數(shù)動(dòng)態(tài)綁定時(shí),根據(jù)對象里保存的vptr指針,查找所指虛函數(shù)表中對應(yīng)函數(shù)項(xiàng),執(zhí)行所指函數(shù)即可實(shí)現(xiàn)動(dòng)態(tài)多態(tài)性。虛函數(shù)表不含指向普通函數(shù)的指針。圖5.7虛函數(shù)實(shí)現(xiàn)原理計(jì)算機(jī)學(xué)院李衛(wèi)明完整代碼見樣例Ex5.8。

樣例中基類CBase具有4個(gè)成員函數(shù):普通成員函數(shù)g1、g2和虛成員函數(shù)f1、f2,派生類CDerived公有繼承基類CBase,也有4個(gè)成員函數(shù):普通成員函數(shù)g1、g3和虛成員函數(shù)f1、f3,其中g(shù)1、f1覆蓋基類同名成員函數(shù),g3、f3是派生類新增成員函數(shù),樣例中通過實(shí)際指向派生類對象的基類指針調(diào)用g1、g2和f1、f2時(shí),g1、g2是普通成員函數(shù),根據(jù)調(diào)用時(shí)指針類型靜態(tài)綁定執(zhí)行基類函數(shù),f1、f2是虛成員函數(shù),根據(jù)調(diào)用時(shí)指針?biāo)笇?shí)際對象動(dòng)態(tài)綁定CDerived類的相應(yīng)函數(shù),CDerived類f1虛成員函數(shù)已覆蓋基類f1虛成員函數(shù),執(zhí)行CDerived類f1虛成員函數(shù),CDerived類繼承了基類f2函數(shù),執(zhí)行CBase類f2函數(shù)。

計(jì)算機(jī)學(xué)院李衛(wèi)明根據(jù)賦值兼容原則,派生類對象指針可以賦值給基類對象指針,內(nèi)部所需轉(zhuǎn)換是在編譯時(shí)進(jìn)行的,是靜態(tài)轉(zhuǎn)換。這樣,同一類族的對象可以統(tǒng)一管理,再配合動(dòng)態(tài)多態(tài)性統(tǒng)一使用。反過來,有時(shí)需要發(fā)揮其中某些派生類對象的派生類特有功能,這時(shí),一般不可以使用編譯時(shí)靜態(tài)轉(zhuǎn)換static_cast將基類對象指針轉(zhuǎn)換成派生類對象指針,因?yàn)榛悓ο笾羔槍?shí)際可能并非指向指定的派生類對象,這樣的static_cast編譯時(shí)靜態(tài)轉(zhuǎn)換不安全。計(jì)算機(jī)學(xué)院李衛(wèi)明5.9.3 *運(yùn)行時(shí)類型識(shí)別RTTI和動(dòng)態(tài)類型轉(zhuǎn)換dynamic_cast下面討論建立在上節(jié)樣例基礎(chǔ)上。CBase*pb,baseObj;CDerived*pd,derivedObj;//CDerived公有繼承baseObjpb=&baseObj;pd=static_cast<CDerived*>(pb);//不安全if(pd!=nullptr)pd->g3();pb=&derivedObjj;pd=static_cast<CDerived*>(pb);//可行if(pd!=nullptr)pd->g3();計(jì)算機(jī)學(xué)院李衛(wèi)明C++提供了運(yùn)行時(shí)類型識(shí)別RTTI(RunTimeTypeIdentification)和動(dòng)態(tài)類型轉(zhuǎn)換dynamic_cast機(jī)制用于解決這一特定需求。C++將含虛函數(shù)的類稱為多態(tài)性類,不含虛函數(shù)的類稱為非多態(tài)性類。每個(gè)多態(tài)性類具有像虛函數(shù)表一樣的類型信息,編譯器一般在多態(tài)性類的虛函數(shù)表中保存類型信息對象,每個(gè)多態(tài)性類的對象有指針指向虛函數(shù)表,因而可獲得的對象的類型信息。C++程序可以通過通過下述2種語句獲得類型信息,獲得的類型信息為type_info類型對象的常引用,type_info類型對象可以進(jìn)行相等比較和通過成員函數(shù)返回類型信息文字描述,使用時(shí)需包含頭文件<typeinfo>:consttype_info&tiObj=typeid(表達(dá)式);consttype_info&tiObj=typeid(類型);當(dāng)表達(dá)式結(jié)果為非多態(tài)性類時(shí),編譯器編譯時(shí)就可以獲取類型信息,一般也無此必要;當(dāng)表達(dá)式結(jié)果為多態(tài)性類對象引用或指針時(shí),上述語句返回相應(yīng)多態(tài)性類的虛函數(shù)表中類型信息對象的常引用,為多態(tài)性類提供了運(yùn)行時(shí)類型識(shí)別RTTI支持。計(jì)算機(jī)學(xué)院李衛(wèi)明C++通過運(yùn)行時(shí)類型識(shí)別RTTI支持,進(jìn)一步支持運(yùn)行時(shí)動(dòng)態(tài)類型轉(zhuǎn)換dynamic_cast:程序運(yùn)行過程中,遇到dynamic_cast基類指針轉(zhuǎn)換時(shí),查詢基類指針?biāo)笇ο蟮膶?shí)際類型信息,基類指針如果實(shí)際指向派生類對象,就可以通過dynamic_cast安全轉(zhuǎn)換為派生類指針,基類指針如果實(shí)際指向的不是派生類對象,dynamic_cast轉(zhuǎn)換返回空指針。下述語句段可以正常獲得預(yù)期結(jié)果:CBase*pb,baseObj;CDerived*pd,derivedObj;//CDerived公有繼承baseObjpb=&baseObj;pd=dynamic_cast<CDerived*>(pb);//安全,實(shí)際返回空指針if(pd!=nullptr)pd->g3();pb=&derivedObjj;pd=dynamic_cast<CDerived*>(pb);//可行,實(shí)際返回派生類對象指針if(pd!=nullptr)pd->g3();計(jì)算機(jī)學(xué)院李衛(wèi)明動(dòng)態(tài)類型轉(zhuǎn)換dynamic_cast同樣適用于基類對象引用。程序運(yùn)行過程中,遇到dynamic_cast基類引用轉(zhuǎn)換時(shí),查詢基類引用對象的實(shí)際類型信息,基類引用的實(shí)際是派生類對象,就可以通過dynamic_cast安全轉(zhuǎn)換為派生類對象引用,

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論