is-a
-----------------siwuxie095
is-a
在 C++ 中,is-a (是一個)的概念就相當於 隱形眼鏡也是眼鏡
如果眼鏡是基類的話,那隱性眼鏡就是眼鏡的派生類
再如:定義人類、工人類、士兵類,其中:工人類和士兵類分別繼承
人類,就可以把每一個工人的對象稱之為 人的對象,也可以把每一個
士兵的對象稱之為 人的對象
基於這種理論,在程序中就有很多更加靈活的玩法了,如下:
先實例化
Soldier 對象 s1,當實例化 Person 對象 p1 時,讓 p1 直接
接收 s1,即 用 s1 去實例化 p1,這樣做,在語法上是正確的,因為一
個士兵也是一個人,用士兵去初始化人是 OK 的,再用 Person 的指針
p2 指向 Soldier 的對象 s1,顯然也是 OK 的
但是,不能說一個人也是一個士兵,即 將人的對象 p1 賦值給士兵的對
象 s1 是有問題的,同時,用士兵的指針 s2 指向人的對象 p1 也是有問
題的
綜上所述:
(1)派生類的對象可以賦值給基類 或 子類的對象可以賦值給父類
(2)基類的指針可以指向派生類的對象
或
父類的指針可以指向子類的對象
既然如此,就可以將基類的指針 或 基類的對象 或 基類的引用,
作為函數的參數,來使函數可以接收所傳入的子類的指針 或 對
象(或者 基類的指針 或 對象)
如下:
存儲結構
(1)將子類的對象賦值給父類的對象,或 用子類的對象初始化
父類的對象
如果父類含有 m_strName 和 m_iAge 兩個數據成員,那麽子類
在繼承父類時,一定也含有 m_strName 和 m_iAge 這兩個數據
成員,同時,子類還有自身的數據成員
當用子類的對象向父類的對象賦值 或 用子類的對象初始化父類的
對象時,其本質就是將子類當中從父類繼承下來的數據成員賦值給
父類的對象,子類當中其它的數據此時就會被截斷,即 丟失
因為,對於父類對象來說,它只能接收自己擁有的數據成員的數據,
而無法接收其它的數據
(2)用父類的指針指向一個子類對象
如果是用父類的指針指向一個子類的對象,那麽父類的指針也
只能訪問到父類所擁有的數據成員,而無法訪問到子類所擁有
的數據成員,即 父類指針指向子類對象時,只能通過父類指針
去訪問父類原有的數據成員和成員函數,無法訪問子類獨有的
數據成員和成員函數
程序 1:
Person.h:
#include <string> using namespace std;
class Person { public: Person(string name = "Jim"); virtual ~Person(); void play(); protected: string m_strName; }; |
Person.cpp:
#include "Person.h" #include <iostream> using namespace std;
Person::Person(string name) { m_strName = name; cout << "Person()" << endl; }
Person::~Person() { cout << "~Person()" << endl; }
void Person::play() { cout << "Person--play()" << endl; cout << m_strName << endl; } |
Soldier.h:
#include "Person.h"
class Soldier:public Person { public: Soldier(string name = "James", int age = 20); virtual ~Soldier(); void work(); protected: int m_iAge; }; |
Soldier.cpp:
#include "Soldier.h" #include <iostream> using namespace std;
Soldier::Soldier(string name,int age) { m_strName = name; m_iAge = age; cout << "Soldier()" << endl; }
Soldier::~Soldier() { cout << "~Soldier()" << endl; }
void Soldier::work() { cout << "Soldier--work()" << endl; cout << m_strName << "," << m_iAge<< endl;
} |
main.cpp:
#include<stdlib.h> #include "Soldier.h" #include <iostream> using namespace std;
int main(void) { Soldier soldier; soldier.work(); cout << endl; //通過父類的對象、指針、引用來指向子類的對象 //即用子類的對象來初始化父類的對象、指針、引用 // //而不能用父類對象去初始化子類 即is-a的關系 //在初始化時父類只會得到子類從父類繼承來的數據成員 //而子類自己的數據成員則會被截斷 丟失 // //假如改為: Person person1; person1=soldier; 這樣就會多執行一次構造函數 Person person1 = soldier; person1.play(); //父類指針也只能指向內存中子類從父類繼承來的數據成員所在的內存 Person *person2 = &soldier; person2->play(); Person &person3 = soldier; person3.play(); cout << endl;
//當Person類的指針從堆中指向Soldier類的對象時 使用虛析構函數 //如果不使用虛析構函數 那麽堆中的內存就無法釋放 導致內存泄露 //即 前面的 virtual 是為下面這段代碼用的 Person *p = new Soldier; p->play(); //如果不使用虛析構函數,delete p 時就只會調用Person類的析構函數, //而指針 p 指向Soldier類對象時卻依次執行了父類和子類的構造函數, //即 子類對象沒有釋放掉 delete p; p = NULL; system("pause"); return 0; }
//當使用父類的指針指向從堆中申請的子類的對象時 //又想要通過父類的指針釋放這塊從堆中申請的內存就必須使用虛析構函數 // //當給父類的析構函數加上關鍵字 virtual 後 這個關鍵字會被繼承下去 //即子類的析構函數也是虛析構函數 即便不寫關鍵字 virtual // 不過推薦子類的析構函數前也寫上 virtual |
運行一覽:
程序 2:
Person.h:
#include <string> using namespace std;
class Person { public: Person(string name = "Jim"); ~Person(); void play(); protected: string m_strName; }; |
Person.cpp:
#include "Person.h" #include <iostream> using namespace std;
Person::Person(string name) { m_strName = name; cout << "Person()" << endl; }
Person::~Person() { cout << "~Person()" << endl; }
void Person::play() { cout << "Person--play()" << endl; cout << m_strName << endl; } |
Soldier.h:
#include "Person.h"
class Soldier :public Person { public: Soldier(string name = "James", int age = 20); ~Soldier(); void work(); protected: int m_iAge; }; |
Soldier.cpp:
#include "Soldier.h" #include <iostream> using namespace std;
Soldier::Soldier(string name, int age) { m_strName = name; m_iAge = age; cout << "Soldier()" << endl; }
Soldier::~Soldier() { cout << "~Soldier()" << endl; }
void Soldier::work() { cout << "Soldier--work()" << endl; cout << m_strName << "," << m_iAge << endl; } |
main.cpp:
#include<stdlib.h> #include "Soldier.h" #include <iostream> using namespace std;
//參數是父類的對象 //因為這裏是對象 在調用test1()時會實例化一個臨時對象(在參數傳進來時) //並通過這個臨時對象來調用play()函數 //test1()執行完畢後臨時對象被銷毀 會執行析構函數 void test1(Person p) { p.play(); }
//參數是父類的引用 //不會產生新的臨時變量 效率更高(推薦) void test2(Person &p) { p.play(); }
//參數是父類的指針 //不會產生新的臨時變量 效率更高(推薦) void test3(Person *p) { p->play(); }
// 在公有繼承中 is-a 的關系在函數參數傳遞時的體現 int main(void) { Soldier s; Person p; cout << endl; test1(s); test1(p); cout << endl; //使用父類的引用作參數也可以接收父類的對象以及子類的對象 test2(s); test2(p); cout << endl; test3(&s); test3(&p); cout << endl; system("pause"); return 0; } |
運行一覽:
【made by siwuxie095】
is-a