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