繼承:程序員
派生類:數組
程序猿種類有不少種,如 C/C++ 程序猿,Java 程序猿,Python 程序猿等等。那麼咱們要把程序猿設計成一個基類, 咱們則須要抽出其特有的屬性和方法。函數
全部程序猿的共同屬性(成員變量):微服務
全部的程序猿都有的共同方法(成員函數):post
而不一樣的程序猿,又有各自不一樣的屬性和方法:大數據
繼承的格式以下:人工智能
class 派生類名:public 基類名 { };
程序猿 Coder
基類:設計
class Coder { public: bool isWorkOvertime(){} // 是否要加班 bool isReward(){} // 是否有獎勵 void Set(const string & name) // 設置名字 { m_name = name; } ... private: string m_name; // 姓名 string m_post; // 職位 int m_sex; // 性別 };
Python 程序猿 PythonCoder
派生類:指針
class PythonCoder : public Coder { public: bool isAIField(){} // 是不是人工智能領域 bool isBigDataField(){} // 是不是大數據領域 };
派生類對象的大小 = 基類對象成員變量的大小 + 派生類對象本身的成員變量的大小。在派生類對象中,包含着基類對象,並且基類對象的存儲位置位於派生類對象新增的成員變量以前,至關於基類對象是頭部。code
class CBase { int a1; int a2; }; class CDerived : public CBase { int a3; };
繼承的關係是「是」的關係:
繼承的關係是「有」的關係:
假設已經存在了 Man 類表示男人,後面須要些一個 Women 類來表示女人。Man 類和 Women 類確實是有共同之處,那麼就讓 Women 類繼承 Man 類,是否合適?
咱們先想一想繼承的邏輯要求,假設 Women 類繼承 Man 類後的邏輯就是:一個女人也是一個男人。很明顯,這顯然不成立!
因此,好的作法是歸納男人和女人的共同特色,抽象出一個 Human 類表示人,而後 Man 和 Woman 都繼承 Human 類。
假設要寫一個小區養狗管理系統:
假定狗只有一個主人,可是一個主人能夠最多有 10 條狗,應該如何設計和使用「主人」類 和「狗」類呢?咱們先看看下面幾個例子:
class CDog; class CMaster // 主人類 { CDog dogs[10]; // 狗類的成員對象數組 }; class CDog // 狗類 { CMaster m; // 主人類的成員對象 };
例子一能夠發現是:
至關於人中有狗,狗中有人:
這樣是很差的,由於會產生循環不斷的構造,主人類構造狗對象,狗類又構造主人對象....
class CDog; class CMaster // 主人類 { CDog * pDogs[10]; // 狗類的對象指針數組 }; class CDog // 狗類 { CMaster m; // 主人類的成員對象 };
這樣又變成狗中有人,人去指向「狗中有人」的狗,關係就會顯得很錯亂,以下圖:
class CDog; class CMaster // 主人類 { CDog dogs[10]; // 狗類的對象數組 }; class CDog // 狗類 { CMaster * pm; // 主人類的對象指針 };
這樣就會變成,人中有狗,人裏面的狗又會指向主人,雖然關係相對好了一點,可是一樣仍是會繞暈,效果以下圖:
class CDog; class CMaster // 主人類 { CDog * pDogs[10]; // 狗類的對象指針數組 }; class CDog // 狗類 { CMaster * pm; // 主人類的對象指針 };
這個是正確的例子,由於至關於人和主人是獨立的,而後經過指針的做用,使得狗是能夠指向一個主人,主人也能夠同時指向屬於本身的 10 個狗,這樣會更靈活。
若是不用指針對象,生成 A 對象的同時也會構造 B 對象。用指針就不會這樣,效率和內存都是有好處的。
好比:
class Car { Engine engine; // 成員對象 Wing * wing; // 成員指針對象 };
定義一輛汽車,全部的汽車都有 engine,但不必定都有 wing
這樣對於沒有 wing 的汽車,wing 只佔一個指針,判斷起來也很方便。
派生類(子類)能夠定義一個和基類(父類)成員同名的成員,這叫「覆蓋」。在派生類(子類)中訪問這類成員時,默認的狀況是訪問派生類中定義的成員。要在派生類中訪問由基類定義的同名成員時,要使用做用域符號::
。
下面看具體的例子:
// 基類 class Father { public: int money; void func(); }; // 派生類 class Son : public Father // 繼承 { public: int money; // 與基類同名成員變量 void func(); // 與基類同名成員函數 void myFunc(); }; void Son::myFunc() { money = 100; // 引用的是派生類的money Father::money = 100; // 引用的是基類的money func(); // 引用的是派生類的 Father::func(); // 引用的是基類的 }
至關於 Son 對象佔用的存儲空間:
咱們都知道基類的 public 成員,都是能夠被派生類成員訪問的,那麼基類的 protected、private 成員,分別能夠被派生類成員訪問嗎?帶着這個問題,咱們能夠先看下面的栗子:
class Father { public: int nPublic; // 公有成員 protected: int nProtected; // 保護成員 private: int nPrivate; // 私有成員 }; class Son : public Father { void func() { nPublic = 1; // OK nProtected = 1; // error nPrivate =1; // ok,訪問從基類繼承的protected成員 Son a; a.nProtected = 1; // error,a不是當前對象 } }; int main() { Father f; Son s; f.nPublic; // OK s.nPublic; // OK f.nProtected; // error s.nProtected; // error f.nPrivate; // error s.nPrivate; // error }
基類的 protected、private 成員對於派生類成員的權限說明:
基類的 protected 成員 | 基類的 private 成員
---|---
派生類的成員函數能夠訪問當前對象的基類的保護成員| 不能被派生類成員訪問
一般在初始化派生類構造函數時,派生類構造函數是要實現初始化基類構造函數的。那麼如何在派生類構造函數裏初始化基類構造函數呢?
class Bug { private : int nLegs; int nColor; public: int nType; Bug (int legs, int color); void PrintBug (){ }; }; class FlyBug : public Bug // FlyBug 是Bug 的派生類 { int nWings; public: FlyBug( int legs,int color, int wings); }; Bug::Bug( int legs, int color) { nLegs = legs; nColor = color; } // 錯誤的FlyBug 構造函數 FlyBug::FlyBug ( int legs,int color, int wings) { nLegs = legs; // 不能訪問 nColor = color; // 不能訪問 nType = 1; // ok nWings = wings; } // 正確的FlyBug 構造函數: FlyBug::FlyBug ( int legs, int color, int wings):Bug( legs, color) { nWings = wings; } int main() { FlyBug fb ( 2,3,4); fb.PrintBug(); fb.nType = 1; fb.nLegs = 2 ; // error. nLegs is private return 0; }
在上面代碼例子中:
第24-30行的派生類構造函數初始化基類是錯誤的方式,由於基類的私有成員是沒法被派生類訪問的,也就沒法初始化。
第33-36行代碼是正確派生類構造函數初始化基類構造函數的方式,經過調用基類構造函數來初始化基類,在執行一個派生類的構造函數
以前,老是先執行基類的構造函數。
從上面的例子中咱們也得知構造派生對象前,是先構造基類對象,那麼在析構的時候依然依據「先構造,後初始化」的原則,因此派生類析構時,會先執行派生類析構函數,再執行基類析構函數。
以下栗子:
class Base { public: int n; Base(int i) : n(i) { cout << "Base " << n << " constructed" << endl; } ~Base() { cout << "Base " << n << " destructed" << endl; } }; class Derived : public Base { public: Derived(int i) : Base(i) { cout << "Derived constructed" << endl; } ~Derived() { cout << "Derived destructed" << endl; } }; int main() { Derived Obj(3); return 0; }
輸出結果:
Base 3 constructed Derived constructed Derived destructed Base 3 destructed
// 基類 class Base {}; // 派生類 class Derived : public Base {}; Base b; // 基類對象 Derived d; // 派生類對象
b = d;
Base & br = d;
Base * pb = & d;
==注意:若是派生方式是 private 或 protected,則上述三條不可行==
// 基類 class Base {}; // 派生類 class Derived : protected Base {}; Base b; // 基類對象 Derived d; // 派生類對象
因此派生方式是 private 或 protected,則是沒法像 public 派生承方式同樣把派生類對象賦值、引用、指針給基類對象。
public 派生方式的狀況下,派生類對象的指針能夠直接賦值給基類指針
Base *ptrBase = & objDerived;
經過強制指針類型轉換,能夠把 ptrBase 轉換成 Derived 類的指針
Base * ptrBase = &objDerived; Derived *ptrDerived = ( Derived * ) ptrBase;
程序員要保證 ptrBase 指向的是一個 Derived 類的對象,不然很容易會出錯。