复习笔记(四)——C++继承
目錄
- 基類與派生類
- 繼承和派生(難點)
- 派生類的聲明
- 類成員的訪問權(quán)限(回顧)
- 基類中的私有成員
- 派生類的三種繼承方式
- 公有繼承(public)
- 受保護(hù)繼承(protected)
- 私有繼承(private)
- 繼承關(guān)系中構(gòu)造和析構(gòu)函數(shù)
- 繼承關(guān)系中構(gòu)造函數(shù)之間的關(guān)系
- 繼承關(guān)系中析構(gòu)函數(shù)之間的關(guān)系
- 繼承中構(gòu)造和析構(gòu)函數(shù)的執(zhí)行順序
- 構(gòu)造和析構(gòu)函數(shù)實例
- 類的層次(難點)
- 派生類重定義基類函數(shù)
- 類指針
- 類指針的使用
- 派生類對象到基類對象的轉(zhuǎn)換
基類與派生類
在原有類的基礎(chǔ)上派生出新的類,新類繼承原有類的屬性和方法,稱原有的類為基類, 又稱為父類。由已存在的類派生出的新類稱為派生類,又稱為子類。
假定有一個類A,要創(chuàng)建一個新類B,它是類A的一個特殊版本。類A就稱為基類,類B則稱為派生類。類A是父,類B就是子類。
單繼承和多繼承
一個派生類可以從一個基類派生,也可以從多個基類派生。從一個基類派生的繼承稱為單繼承;從多個基類派生的繼承稱為多繼承。
繼承和派生(難點)
- 繼承允許以現(xiàn)有的類為基礎(chǔ)來構(gòu)建新類
- 派生類繼承基類的屬性和行為
- 派生類可以修改繼承的屬性和行為
- 派生類可以增加新的屬性和行為
- 派生類對象也是基類對象
- 派生類對象和基類對象可以被統(tǒng)一管理
繼承和派生有什么區(qū)別?
繼承與派生其實是同一過程從不同的角度看,我們將保持已有類的特性而構(gòu)造新類的過程稱為繼承,說白了繼承的目的就是實現(xiàn)原來設(shè)計與代碼的重用,希望盡量利用原有的類。然而當(dāng)新的問題出現(xiàn),原有程序無法解決或不能完全解決時,需要對原有程序進(jìn)行改造,在已有類的基礎(chǔ)上新增自己的特性而產(chǎn)生新類的過程稱為派生。
何時使用繼承?
- 規(guī)則1:如果類A和類B毫不相關(guān),不可以為了使B的功能更多些而讓B繼承A的功能和屬性。
- 規(guī)則2:若在邏輯上B是A的“一種”,則允許B繼承A的功能和屬性。
- 規(guī)則3:看起來很簡單,但是實際應(yīng)用時可能會有意外,繼承的概念在程序世界與現(xiàn)實世界并不完全相同。
所以更加嚴(yán)格的繼承規(guī)則應(yīng)當(dāng)是:若在邏輯上B是A的“一種”,并且A的所有功能和屬性對B而言都有意義,只是B類多了一些獨有的特性,則允許B繼承A的功能和屬性。
繼承的意義:
- 便于管理系統(tǒng)中的對象。
- 系統(tǒng)擴(kuò)充比較容易- 利用程序代碼的再用性。
-加快程序發(fā)展的速度
-減少程序的錯誤
派生類的聲明
單繼承派生類的聲明語法為:
class 派生類名 : 繼承方式 基類名 {派生類新增成員的聲明; };繼承要考慮的部分:①派生類不同于基類的部分;②派生類擴(kuò)充基類的部分。
示例:
class CForm{ public:CForm();~CForm();//其他省略…… }; class CFormMainMenu:public CForm //公有方式繼承 { public:CFormMainMenu();~CFormMainMenu();//其他省略…… };類成員的訪問權(quán)限(回顧)
在類中,我們是設(shè)計師,public, private, protected三種成員可以訪問。
在對象中,我們是使用者,只有規(guī)定的接口public才可以訪問;protected, private不能訪問。
私有成員(private):可以被類自身的成員和友元訪問,但不能被包括派生類在內(nèi)的其他任何類和任何普通函數(shù)訪問 。
公有成員(public):可以被任何普通函數(shù)和任何類的成員函數(shù)訪問。
保護(hù)成員(protected):可以被類自身的成員和友元訪問外,還可以被派生類的成員函數(shù)訪問,但不能被任何非友元的普通函數(shù)訪問
基類中的私有成員
能被派生類繼承
無論是公有繼承還是私有繼承,都不能被派生類的成員函數(shù)直接訪問
能通過基類的public或protect成員函數(shù)訪問。
實例:
#include<iostream> using namespace std;class A { private :int a; public ://構(gòu)造函數(shù),默認(rèn)將a賦值為2;A(){a = 2;}//獲得私有成員a的值int getNum(){return a;} };class B : private A { public://因為是私有繼承,無法訪問基類的公有成員函數(shù),//只能自定義一個成員函數(shù)來訪問該類B從基類A繼承下來的屬性。int get(){return getNum();//getNum函數(shù)是從基類私有繼承下來的私有函數(shù)。} };int main() {B b;cout<<b.get()<<endl; }執(zhí)行結(jié)果: 2派生類的三種繼承方式
類中的成員被派生類繼承后成員對外的可見性會有所不同。
- 公有繼承(public):特點是基類的公有成員和保護(hù)成員作為派生類的成員時,它們都保持原有的狀態(tài),而基類的私有成員仍然是私有的。
- 受保護(hù)繼承(protected):特點是基類的所有公有成員和保護(hù)成員都成為派生類的保護(hù)成員,并且只能被它的派生類成員函數(shù)或友元訪問,基類的私有成員仍然是私有的。
- 私有繼承(private):默認(rèn)繼承方式,特點是基類的公有成員和保護(hù)成員都作為派生類的私有成員,并且不能被這個派生類的子類所訪問。
三種繼承方式的區(qū)別:
公有繼承(public)
基類中公有成員和受保護(hù)成員被繼承后可見性不變
public方式繼承成員存取權(quán)限等級的變化
受保護(hù)繼承(protected)
基類中公有成員和受保護(hù)成員被繼承后都是受保護(hù)的。
私有繼承(private)
基類中公有成員和受保護(hù)成員被繼承后都是私有的
private方式繼承成員存取權(quán)限等級的變化
繼承關(guān)系中構(gòu)造和析構(gòu)函數(shù)
繼承關(guān)系中構(gòu)造函數(shù)之間的關(guān)系
派生類不繼承基類的構(gòu)造函數(shù)
派生類和基類的構(gòu)造函數(shù)之間是一種自動調(diào)用的關(guān)系
創(chuàng)建派生類對象時,派生類構(gòu)造函數(shù)要調(diào)用基類的構(gòu)造函數(shù)對基類的數(shù)據(jù)成員進(jìn)行初始化
-先執(zhí)行基類構(gòu)造函數(shù),然后執(zhí)行派生類構(gòu)造函數(shù)體
-基類構(gòu)造函數(shù)需要參數(shù)時需要顯式調(diào)用,格式:
-基類構(gòu)造函數(shù)不需要參數(shù)時(有默認(rèn)構(gòu)造函數(shù)),隱式調(diào)用
繼承關(guān)系中析構(gòu)函數(shù)之間的關(guān)系
派生類不繼承基類的析構(gòu)函數(shù)
派生類和基類的析構(gòu)函數(shù)之間是一種自動調(diào)用的關(guān)系
派生類的析構(gòu)函數(shù)也需要調(diào)用基類的析構(gòu)函數(shù)做一些和基類相關(guān)的清理工作
銷毀派生類對象時,先執(zhí)行派生類析構(gòu)函數(shù),然后執(zhí)行基類析構(gòu)函數(shù)體
繼承中構(gòu)造和析構(gòu)函數(shù)的執(zhí)行順序
構(gòu)造函數(shù)的執(zhí)行順序:
①先調(diào)用基類的構(gòu)造函數(shù)初始化從基類繼承的數(shù)據(jù)成員
②再執(zhí)行自己的函數(shù)體初始化定義于派生類的數(shù)據(jù)成員
析構(gòu)函數(shù)的執(zhí)行順序:
①派生類的析構(gòu)函數(shù)先執(zhí)行自己的函數(shù)體
②再調(diào)用基類的析構(gòu)函數(shù)
構(gòu)造和析構(gòu)函數(shù)實例
#include<iostream> using namespace std;class People { public:People(char *str); //構(gòu)造函數(shù)~People(); //析構(gòu)函數(shù) protected:char *name; };//構(gòu)造函數(shù)的實現(xiàn) People::People(char *str) {name = new char[strlen(str)+1];strcpy(name, str);cout<<"People construct: "<<name<<endl; } //析構(gòu)函數(shù)的實現(xiàn) People::~People() {cout<<"People destroy: "<<name<<endl;delete []name; }class Teacher : public People { public:Teacher(char *str, char *sch); //構(gòu)造函數(shù)~Teacher(); //析構(gòu)函數(shù) protected:char *school; }; Teacher::Teacher(char *str, char *sch) : People(str) //調(diào)用基類的構(gòu)造函數(shù) { school = new char[strlen(sch)+1];strcpy(school,sch);cout<<"Teacher construct: "<<name<<" in "<<school<<endl; } Teacher::~Teacher() {cout<<"Teacher destroy: "<<name<<" in "<<school<<endl;delete []school; } int main () {People tmp("Zhang San");People p("Li Si");Teacher t("Wang Wu", "FuZhou University");return 0; }執(zhí)行結(jié)果: People construct: Zhang San People construct: Li Si People construct: Wang Wu Teacher construct: Wang Wu in FuZhou University Teacher destroy: Wang Wu in FuZhou University People destroy: Wang Wu People destroy: Li Si People destroy: Zhang San類的層次(難點)
類的層次:
①一個類可以是某個繼承關(guān)系中的基類,也可以是另一個繼承關(guān)系中的派生類
②類A派生出類B,類B又派生出類C,則類B是類C的直接基類,類A是類C的間接基類
定義派生類時,直接基類要明確列出,間接基類不用列出
例子:
class A { protected:int aMember;// 其他省略...... }; class B: protected A { protected:int bMember;// 其他省略...... }; class C: private B { protected:int cMember; };派生類重定義基類函數(shù)
在派生類中重定義基類的函數(shù):
①派生類自動繼承基類的所有成員
②重定義函數(shù)的函數(shù)原型和基類中被重定義函數(shù)的函數(shù)原型必須完全相同
③基類與派生類成員函數(shù)同名可能導(dǎo)致基類的成員函數(shù)被隱藏
④重定義之后兩個函數(shù)共存,但調(diào)用方法不同
-調(diào)用基類函數(shù):基類名 + :: + 函數(shù)名
-調(diào)用派生類函數(shù):直接調(diào)用
如果派生類中于出現(xiàn)相同的成員需要編程時注意其存取的范圍。不能運(yùn)用基類的成員函數(shù)來設(shè)定派生類的數(shù)據(jù)成員, 而只能在派生類中重新定義存取數(shù)據(jù)成員的成員函數(shù)。
重定義實例:
#include <iostream> #include <vector> #include <string.h>using namespace std;class CForm { public:CForm(string title,vector<string>& menu);CForm(string title);void Load(); protected:void PrintTitle();void PrintMenu(); private:string m_title;vector<string> m_menu; }; CForm::CForm(string title) {m_title=title; } CForm::CForm(string title,vector<string> &menu) {m_title = title;m_menu = menu; } void CForm::Load() {PrintTitle();PrintMenu(); } void CForm::PrintMenu() {cout<<"menu = ";for(vector<string>::iterator it = m_menu.begin();it!=m_menu.end();it++){cout<<*it<<" ";}cout<<endl; } void CForm::PrintTitle() {cout<<"m_title = "<<m_title<<endl; }//派生類 class CFormMainMenu:public CForm { public:CFormMainMenu(string title,vector<string>& menu,int color);CFormMainMenu(string title,int color);void Load(); protected: private:int m_color; }; void CFormMainMenu::Load() {CForm::Load();//cout<<"m_menu = "<<m_menu<<endl; //錯誤 想獲得基類的m_menu值,只能通過調(diào)用基類的保護(hù)成員函數(shù)或公有成員函數(shù)cout<<"my background color is yellow!"<<endl; } CFormMainMenu::CFormMainMenu(string title,int color):CForm(title) {m_color=color; } CFormMainMenu::CFormMainMenu(string title,vector<string>& menu,int color):CForm(title,menu) {m_color=color; }int main() {vector<string> menu;menu.push_back("you");menu.push_back("are");menu.push_back("my");menu.push_back("son");CFormMainMenu from("Main Menu",menu,12);from.Load();return 0; }執(zhí)行結(jié)果: m_title = Main Menu you are my son my background color is yellow!類指針
類名也可以用來聲明指針
類的指針可以操作類的對象,也可以操作派生類的對象(派生類對象也是基類對象)
派生類對象和基類對象可以通過指針統(tǒng)一操作和管理
類指針的使用
類指針操作類對象的幾種可能:
①基類指針操作基類對象(自然)
②派生類指針操作派生類對象(自然)
③基類指針操作派生類對象——把派生類對象作為基類對象看(安全)
④派生類指針操作基類對象——把基類對象作為派生類對象看(危險)
派生類對象到基類對象的轉(zhuǎn)換
當(dāng)派生類以Public方式繼承基類時,編譯器可自動執(zhí)行的轉(zhuǎn)換(向上轉(zhuǎn)型 upcasting 安全轉(zhuǎn)換)
①派生類對象自動轉(zhuǎn)換為基類對象(特有的成員消失)
②派生類對象指針自動轉(zhuǎn)化為基類對象指針
③派生類對象引用自動轉(zhuǎn)化為基類對象引用
當(dāng)派生類以private/protected方式繼承基類時:
①派生類對象指針(引用)轉(zhuǎn)化為基類對象指針(引用)需用強(qiáng)制類型轉(zhuǎn)化
(基類&)派生類對象
(基類*)派生類對象指針
②基類對象指針(引用)可用強(qiáng)制類型轉(zhuǎn)換為派生類對象指針(引用), 而基類對象無法執(zhí)行這類轉(zhuǎn)換。
向下轉(zhuǎn)型不安全,沒有自動轉(zhuǎn)換的機(jī)制。
可以使用static_cast運(yùn)算符進(jìn)行強(qiáng)制類型轉(zhuǎn)換,但僅限派生類以public方式繼承基類。
實例:
#include <iostream> #include <string.h> using namespace std;//基類People class People { public:People(char *str, int s); //構(gòu)造函數(shù)中參數(shù)s大于0表示男性,否則為女性~People();int getSex();char* getName(); private:char *name;int sex; }; People::People(char *str, int s) {name = new char[strlen(str)+1];strcpy(name, str);if (s>0)sex = 1; //男性elsesex = 0; //女性 } People::~People() {delete []name; } int People::getSex() {return sex; } char * People::getName() {static char str[128];strcpy(str,name);return str; }//派生類Teacher class Teacher : public People { public:Teacher(char *str, int s, char *sch, int y);~Teacher();int getWorkYears();char *getSchool(); private:char *school;int years; }; Teacher::Teacher(char *str,int s,char *sch,int y):People(str, s) { //調(diào)用基類的構(gòu)造函數(shù)school = new char[strlen(sch)+1];strcpy(school,sch);years = (y>0) ? y : 0; } Teacher::~Teacher() {delete []school; } int Teacher::getWorkYears() {return years; } char * Teacher::getSchool() {static char str[1024];strcpy(str,school);return str; }int main() {People p("Zhang San", 1), *pptr;Teacher t("Li Si",0,"Wuhan University",20), *tptr;pptr = &p;//用基類指針指向基類對象cout<< "People p: " << pptr->getName()<<", "<<(pptr->getSex()?"male":"female")<<endl;pptr=&t;//用基類指針指向派生類對象cout<<"Teacher t: "<<pptr->getName()<<","<<(pptr->getSex()?"male":"female")<<endl;//要調(diào)用定義于派生類中的函數(shù)必須進(jìn)行類型的強(qiáng)制轉(zhuǎn)換cout<<"\tin "<<((Teacher*)pptr)->getSchool()<<"for "<<((Teacher*)pptr)->getWorkYears()<<" years."<<endl;tptr = (Teacher*)&p; //用派生類指針指向基類對象cout<<"People p: "<<tptr->getName()<<", "<<(tptr->getSex()?"male":"female")<<endl;//危險,訪問了不存在的屬性cout<<"\tin(危險,訪問了不存在的屬性) "<<tptr->getSchool()<<" for "<< tptr->getWorkYears()<<" years."<<endl;tptr = &t; //用派生類指針指向派生類對象//調(diào)用基類中定義的函數(shù)cout<<"People p: "<<tptr->getName()<<", "<<(tptr->getSex()?"male":"female")<<endl;cout<<"\tin "<<tptr->getSchool()<<" for "<<tptr->getWorkYears()<<" years."<<endl;return 0; }執(zhí)行結(jié)果: People p: Zhang San, male Teacher t: Li Si,femalein Wuhan Universityfor 20 years. People p: Zhang San, malein(危險,訪問了不存在的屬性) # for 9699072 years. People p: Li Si, femalein Wuhan University for 20 years.總結(jié)
以上是生活随笔為你收集整理的复习笔记(四)——C++继承的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 复习笔记(四)——C++内联函数
- 下一篇: 复习笔记(五)——C++多态和虚函数