人生苟且了很長時間,需要再繼續努力了。
總結了C++的繼承方面的關系:
朋友在面試的時候被問過一個問題,說類的繼承重要的一點是什么,他沒有答到點子上,后來面試官提到的是代碼的復用,不用每次都重新寫相同的代碼,還是有道理的。
類的聲明:
class TableTennisPlayer
{
private:
string firstname;
string lastname;
bool hashTable;
public:
TableTennisPlayer(const string &fn = "none", const string &ln = "none", bool ht=false);
void Name() const; //只讀的一個函數
bool HashTable() const {return hashTable;};
bool ResetTable (bool v) { hashTable = v;};
};
初始化:
TableTennisPlayer play1("zhang", "jingle", true);
string firstname;
string lastname;
bool hashTable;
原型是這樣的
TableTennisPlayer(const string &fn = "none", const string &ln = "none", bool ht=false);
由此引申出來的一點,寫字符串的過程中的賦值過程,應該是調用了 string 類的 以const char *作為參數的構造函數。如果是以string 類為參數的話,則會調用
等等,有一點沒有想明白,形參為const string &, 如果傳遞的參數類型的是string 的話,調用的構造函數是幾次?,如果傳入的參數類型是const char*的話,調用的構造參數又是幾次?
我感覺這方面還是沒有理清楚??
在派生一個類中,如下所示,增加了一個成員變量,對於
class RatedPlayer: public TableTennisPlayer
{
private:
unsigned int rating;
public:
//想起來了,是基本的構造函數
RatedPlayer(unsigned int r = 0, const string &fn = "none", const string &ln = "none", bool ht = false);
//是復制構造函數
RatedPlayer(unsigned int r, const TableTennisPlayer &tp);
unsigned int Rating() const {return rating;};
void Reseat (unsigned int r){rating = r;};
};
派生類的構造函數,
1、首先創建基類對象
2、派生類的構造函數通過成員初始化列表將基類的信息傳遞給基類構造函數。(這個分情況,可能調用基類的構造函數,默認構造函數和復制構造函數)
3、派生類的構造函數應初始化派生類新增的數據成員。
基類的私有部分也是派生類的一部分,但是只能通過基類的公有和保護方法來進行訪問。訪問權限也是很重要的。
基類和派生類的關系:
1、派生類可以使用基類的方法,這個方法不能是私有的。
2、基類指針可以在不進行顯示轉換的情況下指向派生類,基類引用可以在不顯示轉換的情況下,引用派生類對象(向上強制轉換,不能將派生類的指針指向派生類)
但是上面基類的指針卻只能調用基類的方法。
例子:
class base
{
};
class bigger::public base
{
}
bigger hh;
base &rt = hh;
base *rt2 = &hh;
就是這么簡單。
基類指針如果想要調用派生類的方法
1、虛函數
virtual void test()
{
printf("test.....");
};
如果只是虛函數的話,在基類中是要實現的。
在派生類中可以不用重新實現這個類,如果在派生類中沒有實現,則調用基類的方法。如果在派生類重新實現了該方法,則調用的時候使用派生類的方法。
2、純虛函數
純虛函數 virtual test() = 0;
這個是需要在派生類中必須要實現的,帶有純虛函數的基類是不能直接使用的,只是作為一個框架在使用,目的就是讓別人繼承實現多態。
class base
{
public:
virtual dddd() = 0;
};
base aaa; 這樣是不可以的。
class bigger ::public base
{
public:
dddd()
{
xxxxxxxxxx;
}
};
bigger bbbb ;這樣是可以的。
C++中,多態的這種機制才是精華,訪問權限的控制。
如果基類中的數據和方法是privated:
在派生類中,只能調用 基類的方法來訪問基類的訪問權限
虛函數的幾種情況,
1、基類中為虛函數
基類中對虛函數有定義,有實現。
想驗證一下那種是錯誤的寫法:
1.1 派生類 中 對虛函數有定義,帶virtual 無實現
不可以。
1.2派生類中對虛函數有定義,帶virtual 有實現
調用派生類中的方法。
1.3派生類中對虛函數有定義,不帶virtual 無實現
不可以
1.4 派生類中對虛函數有定義,不帶virtual 有實現
調用派生類中方法。
1.4 派生類中對虛函數無定義,不實現。
結果是調用基類的方法。這個是最基本的。
總結一下就是想要在派生類中修改就一定要有定義和實現,感覺自己寫的是廢話,但是定義帶不帶virtual 是不是一樣呢??
自己糾結了這么長時間的問題,百度了一下就知道了,不是必須的,但是為了代碼的清晰易讀,還是加上吧,為了讓孫子類知道,這個是虛函數,呵呵
2、基類中為純虛函數。
基類中不需要實現,但是在派生類中必須要實現。
3、 當虛函數遇上重載。
簡單
class dwelling
{
public:
virtual void showperks() const ;
virtual void showperks(int a)const ;
};
class hovel:public dwelling
{
public:
virtual void showperks() const;
virtual void showperks(int a)const ;//如果此方法注釋的話,也不能調用基類中的該方法,基類中其他版本被隱藏了。
};
當重載遇上虛函數時,想要在派生類中對基類中的函數想要修改的話,就必須在派生類中進行全部重新定義,全部實現。
具體見:/learnCpluseplus/inherit/brass/test1.cpp 。
多態的是實現有兩種方式:
1、一種就是派生類中重新定義基類方法。
2、使用虛函數。
在 上面的例子中 用基類指針指向派生類,如果在基類中定義了一個虛函數,並且在派生類中實現的話(派生類中不管實現的為virtual 還是非virtual ),用基類指針調用該函數,則會調用到派生類中的方法。
看的C++中總結的術語:
如果方法是通過引用或者指針而不是對象調用的,它將確定使用哪種方法,如果沒有使用關鍵字virtual ,程序將只用引用類型或者是指針類型選擇方法,如果使用了virtual,程序將根據引用或指針指向的對象的類型來選擇方法。
class
派生類中國訪問權限的控制:
C++中的權限分為public、protected、private,繼承的方式也有三種,分別是public、protected、private這三種方式
通俗講,派生類中某個屬性或者方法的權限,就是基類中的訪問權限與繼承訪問權限的較小集。(也就是數學中的交集)
在C++中,編譯器對於方法,是靜態聯編還是動態聯編,是跟方法是否定義為虛函數有關系的,編譯器對非虛方法使用的是靜態聯編,對於虛方法是使用動態聯編。
虛函數的工作原理:
對每一個對象都保存一個隱藏的指針,隱藏的指針指向虛函數表。
如果派生類中對基類中的虛函數進行了重新實現,則派生類的虛函數表保存新的函數的地址,
如果派生類中沒有對基類中的虛函數實現,則派生類的虛函數表扔保存基類中的地址。
class base
{
public:
virtual void printA();
virtual void printB()
}
class ::public base
{
void printB();
virtual void printC();
};
總結一點,虛函數實現機制比較好,但是內存和執行速度會有一定的成本。
抽象基類:
這個是一種很高深的能力,在設計基類的時候應該先構建出出編程中所需要的類,以及他們之間的關系,抽象基類更清晰、復雜度更低,在基於組件的編程模型中很常見。
繼承和內存中的分配,這個剛開始沒有搞清楚,當然現在也沒有搞清楚。
抽象能力。