組合關係是總體與部分的關係。
組合關係的特色:
A、將其它類的對象做爲當前類的成員使用
B、當前類的對象與成員對象的生命週期相同
C、成員對象在用法上與普通對象相同
Computer類由其它多個部件類組合而成,當Computer銷燬時,其它部件對象同時銷燬。ios
#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; }
繼承關係是類之間的父子關係。繼承關係的特色以下:
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; }
定義類時根據類的設計需求肯定成員的訪問級別,規則以下:
A、public修飾的成員能夠被外部訪問。
B、protected修飾的成員不能夠被外部訪問,但能夠被子類訪問。
C、private修飾的成員不能夠被外部和子類訪問。ide
#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; }
在C++編程中,軟件可重用性(software reusability)是經過繼承(inheritance)機制來實現的。類的繼承,是新的類從已有類獲得已有的特性。從已有類產生新類的過程就是類的派生。原有的類稱爲基類或父類,產生的新類稱爲派生類或子類。
繼承是一種封裝模型之間關係的抽象,是不一樣封裝模型的層次分類。函數
派生類的聲明:學習
class 派生類名:[繼承方式] 基類名 { 派生類成員聲明; };
若是一個派生類同時有多個基類,稱爲多重繼承;若是派生類只有一個基類,稱爲單繼承。this
繼承方式規定了如何訪問基類繼承的成員。繼承方式有public、 private,、protected。繼承方式指定了派生類成員以及類外對象對於從基類繼承來的成員的訪問權限。繼承方式以下:
A、公有繼承
基類的公有成員和保護成員在派生類中保持原有訪問屬性,其私有成員仍爲基類的私有成員。
B、私有繼承
基類的公有成員和保護成員在派生類中成了私有成員,其私有成員仍爲基類的私有成員。
C、保護繼承
基類的公有成員和保護成員在派生類中成了保護成員,其私有成員仍爲基類的私有成員。
不一樣繼承方式下父類成員的訪問級別以下:
繼承成員的訪問屬性 = Max{繼承方式,父類成員的訪問屬性}spa
使用class關鍵字定義派生類時,默認繼承方式爲private。
使用struct關鍵字定義派生類時,默認繼承方式爲public。
C++工程項目中,一般只使用public繼承方式。設計
#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; }
派生類中的成員包含兩大部分,一類是從基類繼承過來的,一類是本身增長的成員。從基類繼承的成員表現其共性,而新增的成員體現其個性。
派生類中由基類繼承而來的成員的初始化工做仍是由基類的構造函數完成,而後派生類中新增的成員在派生類的構造函數中初始化。派生類沒有繼承基類的構造函數和析構函數。指針
派生類的構造函數語法以下:code
派生類名::派生類名(參數總表) :基類名(參數表),內嵌子對象(參數表) { 派生類新增成員的初始化語句; //也可出如今參數列表中 }
子類構造函數必須對繼承來的成員進行初始化,能夠直接經過初始化列表或賦值方式進行,也能夠調用父類構造函數進行初始化。
父類構造函數在子類中的調用方式:
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; }
派生類拷貝構造函數的定義以下:
派生類::派生類(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; }
派生類的賦值操做符重載函數的定義以下:
子類& 子類::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; }
派生類的析構函數的功能是在對象銷燬前進行一些必要的清理工做,析構函數沒有類型,也沒有參數。析構函數的調用順序與構造函數相反。
析構函數只有一種,無重載,無默參。
子類對象銷燬時析構函數的調用順序以下:
A、調用類自身的析構函數
B、調用成員變量的析構函數
C、調用父類的析構函數
4、父類與子類的同名覆蓋
子類定義父類中的同名成員時發生同名覆蓋,規則以下:
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; }
賦值兼容原則是指子類對象能夠做爲父類對象使用。
賦值兼容原則的特色以下:
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; }
子類中重定義父類中已經存在的成員函數,稱爲函數重寫。
函數重寫是同名覆蓋的一種特殊狀況。
#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++編譯器認爲父類指針指向的是父類對象,父類引用是對父類對象的引用,所以編譯結果只能是調用父類中定義的同名函數。