1、類和對象
類就是對對象的描述,主要從屬性和行為兩個方面描述。
對於屬性一般作成private , 行為作為public
函數 (1)建構函式,初始化所有的成員變數,系統自動調用,可以重載
(2)解構函式,在對象生命週期結束的時候自動被調用調用,不準重載
建構函式和解構函式都是系統自動調用的,解構函式可以通過對象調用
A a;
a.A(); //error 建構函式是不能手工調用的
a.~A(); //right 手工調用解構函式時,會被當作一個普通的成員函數調用,其中的代碼會被執行,對象不被銷毀
(3)get,set方法 用於訪問私人的成員變數的,外界訪問變數的唯一通道
(4)類本身的行為 是我們在編碼時要下功夫的地方
2、類的組成
(1)資料部分
(2)建構函式和解構函式
(3)get & set方法
(4)業務方法
3、棧的實現(儲存int類型的棧)
要想實現,需要一個數組做容器,儲存使用者插入的資料,用指標實現; int *p;
還需要一個變數,儲存棧頂位置 int top; //top從0開始
對於棧不能無限制的存資料,所以需要一個int類型的變數來記載數組的最大長度 int max;
(1)建構函式
當不給參數的時候,棧的長度是預設值Stack();也可以使用者指定長度Stack(int)
(2)解構函式
一定要有,因為要在堆中申請空間
(3)為保護棧資料的安全,對p不能提供get,set 方法
對於top,max的值,取決於插入的資料,所有只能有get方法,不能設定,不需要有set方法
(4)int push(int); 插入,當棧滿的時候,返回-1,表示插入失敗
int pop(); 刪除棧頂的元素,如果正確刪除一個資料,返回資料的值,若沒有正確刪除,返回-1,表示棧空了
void disp(); 現實資料
(5)實現的時候,注意:
在函數名前加 " Stack:: "
(6)在棧中插入資料,就是在top的位置插入資料
刪除資料,就是把棧頂下移一個位置
4、繼承
(1)繼承表現一種" is a " 的關係。Teacher extend Person
其中Teacher是子類,Person是父類
子類繼承父類的所有屬性和行為
(2)class Teacher:public Person{};
表示Teacher類繼承Person類
在extends_sample2中,子類可以直接使用父類的teach函數,但是不能使用父類private的name屬性。name屬性實際上存在在子類中,但是不能被子類直接使用,因為是private屬性。private屬性只能被本對象的函數訪問,public的屬性可以被子類直接使用,但是外部函數也可以訪問public許可權的屬性。既想被子類使用又不想讓外部函數訪問,怎麼辦哪?使用protected修飾屬性就可以了。
下面是一個類的屬性具有不同的存取控制修飾符在不同的對象中的可見度i(可見度就是可以訪問):
本對象 子類對象 其他函數
private屬性 可見 不可見 不可見
protected屬性 可見 可見 不可見
public屬性 可見 可見 可見
在繼承關鍵字extends前面也有三種不同的存取控制修飾符號,被不同的繼承方式繼承後存取控制許可權會發生變化。可以把繼承方式理解成城門。無論外邊的人多麼胖,想通過不同寬度的城門只能減肥到相應的寬度才可以。
public extends protected extends private extends
父類的private屬性 不能訪問 不能訪問 不能訪問
父類的protected屬性 變成protected 不變 變成private,子類可以訪問,子類的子類不能訪問
父類的public屬性 不變 變成protected 變成private,子類可以訪問,子類的子類不能訪問
建構函式不能被繼承。因為建構函式只是適合於本類的產生方式。
如extends_sample4,建立子類的時候需要首先建立父類。怎麼理解哪? 考慮子類的構成,就像cs中的匪徒用的46是由ak47添加上瞄準鏡組成的。
建立子類的時候會首先調用父類的建構函式,因為繼承的時候沒有指定繼承時使用的父類的建構函式。 建構函式有很多種,因為沒有指定建構函式,就會預設使用無參的建構函式。如果父類沒有無參的建構函式,那麼就會出現編譯錯誤。
這是問題的產生,如何解決哪? 可以在父類中添加無參建構函式。如果我們不是父類的設計者,就應該在子類繼承的時候指定使用父類的那個建構函式。 如在寫子類建構函式時,使用這種形式Teacher(char* name, int age, double salary):Person(name,age){......},就可以指定使用父類的有參建構函式。
構造時候是先父類後子類。解構函式哪?解構函式不能被繼承。子類釋放的時候,首先調用自身的解構函式,再調用父類的解構函式。這與建構函式的調用順序相反。
5、在子類中可以修改父類的行為,叫方法的覆蓋
(1)在子類中的函數名必須與父類的一樣
(2)隱藏,無法產生多態
class Base{ public: void fn( int a ){ cout<<"Base a = " << a << endl; } };
class Sub:public Base{ public: void fn( ){ cout<<"Sub b = 20" << endl; } };
(3)調用父類的方法
a.Base::fn(10); //可以有針對性的調用父類的函數
6、函數的高內聚,低耦合
高內聚:函數的功能盡量單一,這樣的代碼可維護性高
低耦合:避免修改函數的時候,產生連鎖反映。
7、多態
(1)什麼是多態?
一個int類型的指標,只能指向一個int類型的變數
對於對象來講Person類型的指標 ,能指向Person類型的對象
Person類型的指標能指向一個Teacher(extend Person)類型的對象
(2)多態的特徵:
父類的指標可以指向子類的對象
通過父類指標只能調用父類中聲明的方法
通過指標調用函數的時候,若函數是個虛函數,表現出來的行為是對象自身的行為
Person *p = new Teacher ;
編譯時間類型 運行時類型
(3)子類的指標不能指向父類對象
因為子類中會有子類特有的函數,運行時不能通過指標調用子類特有的函數
(4)" virtual "父類函數的傳回值前加此關鍵字,則為虛函數
(5)產生多態的必要前提:
繼承,方法覆蓋,虛函數
8、虛函數的實現原理 在每個對象中都有一個虛函數列表的指標,虛函數列表是一個棧。 在構造對象時,根據先構造父類,再構造子類的原則,父類的函數先入棧,在子類中覆蓋的函數放在上面。 等物件建構完畢時,子類的函數在最上面,根據棧後進先出的原則,先調用的是子類函數
9、在釋放資源的時候 delete p ; 只會調用Person類的解構函式,因為指標的類型是Person的,這樣會造成子類的空間得不到及時的釋放,會造成記憶體泄露 把解構函式也寫成虛函數,這樣就會先調用子類的解構函式,再析構父類的解構函式 在繼承關係中,父類的解構函式必須是虛函數!!!
10、多態的使用
#include <iostream> using namespace std; class Person{ public: virtual double buy(){ return 2 ; } }; class Teacher : public Person{ public: virtual double buy(){ return 1 ; } }; class Student : public Person{ public: virtual double buy(){ return 0.5 ; } }; class CEO : public Person{ public: virtual double buy(){ return 1000 ; } }; void shoufei( Person * p ){ cout<< p->buy() << endl; } int main(){ Person p ; Teacher t ; Student s ; CEO c ; shoufei( &p ) ; //通過傳入不同對象的地址,調用相應的函數 shoufei( &t ) ; //與if...else對比 shoufei( &s ) ; //寫程式要盡量少改動寫好的源碼,這樣實現代碼的通用及低耦合 shoufei( &c ) ; return 0 ; }
作業:寫一個校車類,用數組裝乘客Bus
Bus : 上車,下車 , 賣票等行為
車上有Teacher,Student ,計算總票價