標籤:alt 複用 computer count 初始化 如何 png 類的聲明 重定義
C++語言學習(十)——繼承與派生一、類之間的關係1、類之間的組合關係
組合關係是整體與部分的關係。
組合關係的特點:
A、將其它類的對象作為當前類的成員使用
B、當前類的對象與成員對象的生命週期相同
C、成員對象在用法上與普通對象相同
Computer類由其它多個組件類組合而成,當Computer銷毀時,其它組件對象同時銷毀。
#include <iostream>using namespace std;class Memory{public: Memory() { cout << "Memory()" << endl; } ~Memory() { cout << "~Memory()" << endl; }};class Disk{public: Disk() { cout << "Disk()" << endl; } ~Disk() { cout << "~Disk()" << endl; }};class CPU{public: CPU() { cout << "CPU()" << endl; } ~CPU() { cout << "~CPU()" << endl; }};class MainBoard{public: MainBoard() { cout << "MainBoard()" << endl; } ~MainBoard() { cout << "~MainBoard()" << endl; }};class Computer{ Memory mMem; Disk mDisk; CPU mCPU; MainBoard mMainBoard;public: Computer() { cout << "Computer()" << endl; } void power() { cout << "power()" << endl; } void reset() { cout << "reset()" << endl; } ~Computer() { cout << "~Computer()" << endl; }};int main(int argc, char *argv[]){ Computer c; c.reset(); return 0;}
2、類之間的繼承關係
繼承關係是類之間的父子關係。繼承關係的特點如下:
A、子類擁有父類的所有屬性和行為
B、子類也是一種特殊的父類
C、子類對象可以當父類對象使用
D、子類中可以添加父類沒有的屬性和方法
E、子類對象可以直接初始化為父類對象
F、子類對象可以直接賦值給父類對象
G、繼承是物件導向編程中代碼複用的重要手段
#include <iostream>using namespace std;class Parent{public: Parent(int i = 0) { member = i; } void method() { cout << "member = " << member << endl; }private: int member;};class Child : public Parent{public: Child(int i = 0, int j = 0):Parent(i) { childMember = j; } void childMethod() { method(); cout << "childMember = "<< childMember << endl; }private: int childMember;};int main(int argc, char *argv[]){ Child child(1,2); child.method(); child.childMethod(); return 0;}
3、類成員的存取層級選擇
定義類時根據類的設計需求確定成員的存取層級,規則如下:
A、public修飾的成員可以被外部存取。
B、protected修飾的成員不可以被外部存取,但可以被子類訪問。
C、private修飾的成員不可以被外部和子類訪問。
#include <iostream>using namespace std;class Parent{public: Parent(int a = 0, int b = 0, int c = 0) { pub = a; pro = b; pri = c; } void method() { cout << "pub = " << pub << endl; cout << "pro = " << pro << endl; cout << "pri = " << pri << endl; }public: int pub;protected: int pro;private: int pri;};class Child : public Parent{public: Child(int a = 0, int b = 0):Parent(a, b) { } void childMethod() { pub = 100; pro = 200; //pri = 300;//error,子類不可見 }};int main(int argc, char *argv[]){ Parent parent(1,2); parent.pub = 1000; //parent.pro = 2000;//error,外部不可見 //parent.pri = 3000;//error,外部不可見 Child child(1,2); child.pub = -1000; //child.pro = -2000;//error,外部不可見 //child.pri = -3000;//error,外部不可見 child.childMethod(); return 0;}
二、繼承1、繼承簡介
在C++編程中,軟體可重用性(software reusability)是通過繼承(inheritance)機制來實現的。類的繼承,是新的類從已有類得到已有的特性。從已有類產生新類的過程就是類的派生。原有的類稱為基類或父類,產生的新類稱為衍生類別或子類。
繼承是一種封裝模型之間關係的抽象,是不同封裝模型的層次分類。
2、衍生類別的定義
衍生類別的聲明:
class 衍生類別名:[繼承方式] 基類名{ 衍生類別成員聲明;};
如果一個衍生類別同時有多個基類,稱為多重繼承;如果衍生類別只有一個基類,稱為單繼承。
3、繼承方式
繼承方式規定了如何訪問基類繼承的成員。繼承方式有public、 private,、protected。繼承方式指定了衍生類別成員以及類外對象對於從基類繼承來的成員的存取權限。繼承方式如下:
A、公有繼承
基類的公有成員和保護成員在衍生類別中保持原有訪問屬性,其私人成員仍為基類的私人成員。
B、私人繼承
基類的公有成員和保護成員在衍生類別中成了私人成員,其私人成員仍為基類的私人成員。
C、保護繼承
基類的公有成員和保護成員在衍生類別中成了保護成員,其私人成員仍為基類的私人成員。
不同繼承方式下父類成員的存取層級如下:
繼承成員的訪問屬性 = Max{繼承方式,父類成員的訪問屬性}
4、預設繼承方式
使用class關鍵字定義衍生類別時,預設繼承方式為private。
使用struct關鍵字定義衍生類別時,預設繼承方式為public。
C++工程項目中,通常只使用public繼承方式。
5、不同繼承方式樣本
#include <iostream>using namespace std;class Parent{public: Parent(int a = 0, int b = 0, int c = 0) { pub = a; pro = b; pri = c; }public: int pub;protected: int pro;private: int pri;};class ChildA : public Parent{public: ChildA(int a = 0, int b = 0):Parent(a, b) { } void print() { cout << "pub = " << pub << endl; cout << "pro = " << pro << endl; //cout << "pri = " << pri << endl;//error,私人成員不可見 }};class ChildB : protected Parent{public: ChildB(int a = 0, int b = 0):Parent(a, b) { } void print() { cout << "pub = " << pub << endl; cout << "pro = " << pro << endl; //cout << "pri = " << pri << endl;//error,私人成員不可見 }};class ChildC : private Parent{public: ChildC(int a = 0, int b = 0):Parent(a, b) { } void print() { cout << "pub = " << pub << endl; cout << "pro = " << pro << endl; //cout << "pri = " << pri << endl;//error,私人成員不可見 }};//預設繼承方式class ChildD : Parent{public: ChildD(int a = 0, int b = 0):Parent(a, b) { } void print() { cout << "pub = " << pub << endl; cout << "pro = " << pro << endl; //cout << "pri = " << pri << endl;//error,私人成員不可見 }};int main(int argc, char *argv[]){ ChildA childa(1000,2000); childa.pub = 0; //childa.pro = 2000;//error,外部不可見 //childa.pri = 3000;//error,外部不可見 childa.print(); ChildB childb(1001,2001); //childb.pub = 1001;//error,外部不可見 //childb.pro = 2001;//error,外部不可見 //childb.pri = 3001;//error,外部不可見 childb.print(); ChildC childc(1002,2002); //childc.pub = 1002;//error,外部不可見 //childc.pro = 2002;//error,外部不可見 //childc.pri = 3002;//error,外部不可見 childc.print(); ChildD childd(1003,2003); //childd.pub = 1003;//error,外部不可見 //childd.pro = 2003;//error,外部不可見 //childd.pri = 3003;//error,外部不可見 childd.print(); return 0;}
三、衍生類別1、衍生類別簡介
衍生類別中的成員包含兩大部分,一類是從基類繼承過來的,一類是自己增加的成員。從基類繼承的成員表現其共性,而新增的成員體現其個性。
衍生類別中由基類繼承而來的成員的初始化工作還是由基類的建構函式完成,然後衍生類別中新增的成員在衍生類別的建構函式中初始化。衍生類別沒有繼承基類的建構函式和解構函式。
2、衍生類別的建構函式
衍生類別的建構函式文法如下:
衍生類別名::衍生類別名(參數總表):基類名(參數表),內嵌子物件(參數表){ 衍生類別新增成員的初始化語句; //也可出現在參數列表中}
子類建構函式必須對繼承來的成員進行初始化,可以直接通過初始化列表或賦值方式進行,也可以調用父類建構函式進行初始化。
父類建構函式在子類中的調用方式:
A、預設調用,適用於無參建構函式和使用預設參數的建構函式
B、顯示調用,適用於所有父類建構函式,通過初始化列表調用
#include <iostream>#include <string>using namespace std;class Parent{public: Parent() { cout << "Parent()" << endl; } Parent(string s) { cout << "Parent(string s): " << s << endl; } ~Parent() { cout << "~Parent()" << endl; }};class Child : public Parent{public: //隱式調用父類的無參建構函式或預設參數建構函式 Child() { cout << "Child()" << endl; } //顯示調用父類建構函式,如果不顯示調用,預設調用 //父類的無參建構函式或預設參數建構函式 Child(string s):Parent(s) { name = s; cout << "Child(): " << s << endl; } ~Child() { cout << "~Child(): " << name << endl; }private: string name;};int main(int argc, char *argv[]){ Child child1("bauer"); // output: // Parent(string s): bauer // Child(): bauer // ~Child(): bauer // ~Parent() return 0;}
建構函式的調用順序不以初始化列表中的調用順序進行,而是根據類中對成員變數聲明的順序進行調用。如果基類中沒有預設建構函式(無參),那麼在衍生類別的建構函式中必須顯示調用基類建構函式,以初始化基類成員。
衍生類別對象建立時建構函式的調用順序如下:
A、調用基類建構函式,調用順序按照基類被繼承時聲明的順序(從左至右);
B、調用成員變數的建構函式,調用順序按照成員變數在類中聲明的順序;
C、調用衍生類別自身的建構函式。
子類建構函式中,要麼顯示的調用父類的建構函式(傳參),要麼隱式的調用。發生隱式調用時,父類要有無參建構函式或是可以包含無參建構函式的預設參數函數。子類建構函式必須對繼承而來的成員進行初始化,可以通過初始化列表或者賦值的方法進行初始化,也可以通過調用父類建構函式進行初始化。
子類對象建立時建構函式的調用順序如下:
A、調用父類的建構函式
B、調用成員變數的建構函式
C、調用類自身的建構函式
#include <iostream>using namespace std;class Parent{public: Parent() { cout << "Parent()" << endl; } Parent(string s) { cout << "Parent(string s): " << s << endl; } ~Parent() { cout << "~Parent()" << endl; }};class Member{public: Member(int i = 0) { this->i = i; cout << "Member(int i = 0) i = " << i << endl; } ~Member() { cout << "~Member() i = " << i << endl; }private: int i;};class Child : public Parent{public: //隱式調用父類的無參建構函式或預設參數建構函式 Child() { cout << "Child()" << endl; } //顯示調用父類建構函式,如果不顯示調用,預設調用 //父類的無參建構函式或預設參數建構函式 Child(string s, int i):Parent(s),member(i) { name = s; cout << "Child(): " << s << endl; } ~Child() { cout << "~Child(): " << name << endl; }private: string name; Member member;};int main(int argc, char *argv[]){ Child childA("bauer", 10); // output: // Parent(string s): bauer // Member(int i = 0) i = 10 // Child(): bauer // ~Child(): bauer // ~Member() i = 10 // ~Parent() return 0;}
3、衍生類別的拷貝建構函式
衍生類別拷貝建構函式的定義如下:
衍生類別::衍生類別(const 衍生類別& another):基類(another),衍生類別新成員(another.新成員){ }
衍生類別中的預設拷貝建構函式會調用父類中預設或自實現拷貝建構函式,若衍生類別中自實現拷貝建構函式,則必須顯示的調用父類的拷貝建構函式。
#include <iostream>#include <string>using namespace std;class Student{public: Student(string sn,int n,char s) { name = sn; num = n; sex = s; cout << "Student(string sn,int n,char s)" << endl; } Student(const Student& another) { name = another.name; num = another.num; sex = another.sex; } ~Student() { cout << "~Student()" << endl; } void print() { cout << name << endl; cout << num << endl; cout << sex << endl; }private: string name; int num; char sex;};class Graduate : public Student{public: Graduate(string sn,int in,char cs,int fs):Student(sn, in, cs) { salary = fs; cout << "Graduate(string sn,int in,char cs,float fs)" << endl; } ~Graduate() { cout << "~Graduate()" << endl; } Graduate(const Graduate& another):Student(another) { salary = another.salary; } void display() { print(); cout<<salary<<endl; }private: int salary;};int main(int argc, char *argv[]){ Graduate s("bauer", 1001,‘M‘,8000); s.display(); Graduate g = s; g.display(); return 0;}
4、衍生類別的賦值運算子多載
衍生類別的賦值操作符重載函數的定義如下:
子類& 子類::operator=(const 子類& another){ if(this != &another){ 父類::operator =(another); // 調用父類的賦值運算子多載 this->salary = another.salary;//子類成員初始化} return * this;}
衍生類別的如果沒有實現賦值操作符函數,C++編譯器會提供一個預設賦值操作符重載函數,預設的賦值操作符重載函數會調用父類的賦值操作符重載函數(編譯器提供的預設賦值操作符重載函數或是開發人員提供的賦值操作符重載函數)。
#include <iostream>#include <string>using namespace std;class Student{public: Student(string sn,int n,char s) { name = sn; num = n; sex = s; cout << "Student(string sn,int n,char s)" << endl; } Student(const Student& another) { name = another.name; num = another.num; sex = another.sex; } Student& operator = (const Student& another) { if(this != &another) { name = another.name; num = another.num; sex = another.sex; } return *this; } ~Student() { cout << "~Student()" << endl; } void print() { cout << name << endl; cout << num << endl; cout << sex << endl; }private: string name; int num; char sex;};class Graduate : public Student{public: Graduate(string sn,int in,char cs,int fs):Student(sn, in, cs) { salary = fs; cout << "Graduate(string sn,int in,char cs,float fs)" << endl; } ~Graduate() { cout << "~Graduate()" << endl; } Graduate(const Graduate& another):Student(another) { salary = another.salary; } void display() { print(); cout<<salary<<endl; }private: int salary;};int main(int argc, char *argv[]){ Graduate s("bauer", 1001,‘M‘,8000); cout << endl; s.display(); cout << endl; Graduate g("lee", 1002, ‘F‘, 9000); cout << endl; g.display(); cout << endl; g = s; g.display(); cout << endl; return 0;}
衍生類別如果實現了賦值操作符重載函數,則需在賦值操作符重載函數內顯示地調用父類的賦值操作符重載函數。
#include <iostream>#include <string>using namespace std;class Student{public: Student(string sn,int n,char s) { name = sn; num = n; sex = s; cout << "Student(string sn,int n,char s)" << endl; } Student(const Student& another) { name = another.name; num = another.num; sex = another.sex; } Student& operator = (const Student& another) { if(this != &another) { name = another.name; num = another.num; sex = another.sex; } return *this; } ~Student() { cout << "~Student()" << endl; } void print() { cout << name << endl; cout << num << endl; cout << sex << endl; }private: string name; int num; char sex;};class Graduate : public Student{public: Graduate(string sn,int in,char cs,int fs):Student(sn, in, cs) { salary = fs; cout << "Graduate(string sn,int in,char cs,float fs)" << endl; } ~Graduate() { cout << "~Graduate()" << endl; } Graduate(const Graduate& another):Student(another) { salary = another.salary; } Graduate& operator = (const Graduate& another) { if(this != &another) { Student::operator =(another); salary = another.salary; } return *this; } void display() { print(); cout<<salary<<endl; }private: int salary;};int main(int argc, char *argv[]){ Graduate s("bauer", 1001,‘M‘,8000); cout << endl; s.display(); cout << endl; Graduate g("lee", 1002, ‘F‘, 9000); cout << endl; g.display(); cout << endl; g = s; g.display(); cout << endl; return 0;}
5、衍生類別的解構函式
衍生類別的解構函式的功能是在對象銷毀前進行一些必要的清理工作,解構函式沒有類型,也沒有參數。解構函式的調用順序與建構函式相反。
解構函式只有一種,無重載,無默參。
子類對象銷毀時解構函式的調用順序如下:
A、調用類自身的解構函式
B、調用成員變數的解構函式
C、調用父類的解構函式
四、父類與子類的同名覆蓋
子類定義父類中的同名成員時發生同名覆蓋,規則如下:
A、子類可以定義父類中的同名成員(成員變數和成員函數)
B、子類中的成員將隱藏父類中的同名成員
C、父類中的同名成員依然存在於子類中
D通過範圍訪問符訪問父類中的同名成員
如果某衍生類別的多個基類擁有同名的成員,衍生類別又新增與基類同名的成員,衍生類別成員將shadow(隱藏)所有基類的同名成員,需要範圍的調用方式才能調用基類的同名成員。
#include <iostream>using namespace std;class Parent{public: int m_count; void print() { cout << &m_count << endl; }};class Child : public Parent{public: int m_count; void print() { cout << &(Parent::m_count) << endl; cout << &m_count << endl; }};int main(int argc, char *argv[]){ Parent p; p.print(); cout << &p.m_count << endl; Child child; child.print(); //子類對象的父類同名成員變數訪問 cout << &child.Parent::m_count <<endl; //子類對象的父類同名成員函數訪問 child.Parent::print(); cout << &child.m_count << endl; return 0;}
函數重載發生在同一範圍 ,父類和子類的同名函數不構成函數重載,屬於同名覆蓋,子類會隱藏父類中的同名成員。
子類可以定義父類中的同名成員,子類中的成員將隱藏父類中的同名成員,父類中的同名成員依然存在子類中,通過範圍分辨符::訪問父類中的同名成員。
子類中的成員函數將隱藏父類中的同名成員函數,子類無法重載父類中同名成員函數,使用範圍分辨符可以訪問父類中的同名成員函數。
#include <iostream>using namespace std;class Parent{public: int mi; void add(int i) { mi += i; } void add(int a, int b) { mi += (a + b); }};class Child : public Parent{public: int mi; void add(int x, int y, int z) { mi += (x + y + z); }};int main(int argc, char *argv[]){ Parent p; p.add(1); p.add(1,2); Child child; child.add(1,2,3); //child.add(1);//error child.Parent::add(1); //child.add(1,2);//error child.Parent::add(1,2); return 0;}
五、賦值相容原則1、賦值相容原則簡介
賦值相容原則是指子類對象可以作為父類對象使用。
賦值相容原則的特點如下:
A、子類對象可以直接賦值給父類對象
B、子類對象可以直接初始化父類對象
C、父類指標可以直接指向子類對象
D、父類引用可以直接引用子類對象
當使用父類指標指向子類對象、父類引用對子類對象進行引用時,子類對象退化為父類對象,只能訪問父類中定義的成員,因此可以直接存取被子類覆蓋的同名成員。
#include <iostream>using namespace std;class Parent{public: int mi; void add(int i) { mi += i; } void add(int a, int b) { mi += (a + b); }};class Child : public Parent{public: int mi; void add(int x, int y, int z) { mi += (x + y + z); }};int main(int argc, char *argv[]){ Child child; Parent p = child; p.add(1); p.add(1,2); Parent& rp = child; rp.add(1); rp.add(1,2); //rp.add(1,2,3);//error Parent* pp = &child; pp->add(1); pp->add(1,2); //pp->add(1,2,3);//error return 0;}
2、函數重寫
子類中重定義父類中已經存在的成員函數,稱為函數重寫。
函數重寫是同名覆蓋的一種特殊情況。
#include <iostream>using namespace std;class Parent{public: int mi; void add(int i) { mi += i; } void add(int a, int b) { mi += (a + b); } void print() { cout << "Parent" << endl; }};class Child : public Parent{public: int mi; //函數重寫 void add(int x, int y) { mi += (x + y); } //函數重寫 void print() { cout << "Child" << endl; }};int main(int argc, char *argv[]){ Child child; Parent p = child; p.add(1); p.add(1,2); Parent& rp = child; rp.add(1); rp.add(1,2); rp.print();//Parent Parent* pp = &child; pp->add(1); pp->add(1,2); pp->print();//Parent return 0;}
C++是一種靜態編譯語言,在編譯期間,C++編譯器只能根據指標的類型判斷所指向的對象。根據賦值相容原則,C++編譯器認為父類指標指向的是父類對象,父類引用是對父類對象的引用,因此編譯結果只能是調用父類中定義的同名函數。
C++語言學習(十)——繼承與派生