物件導向程式設計 (object-oriented programming) 有三大基本特性,分別是封裝 (encapsulation) 、繼承 (inheritance) 及多型 (polymorphism)
繼承的目的是讓類別 (class) 具有像是親屬的垂直關係(父母子女),子類別 (subclass) 可以擁有父類別 (superclass) 的成員 (member) ,而多型像是親屬的平行關係(兄弟姊妹),多個子類別繼承自單一父類別之時,這些子類別就可以用父類別代替,父類別如同家族裡的「姓」,子類別則是「名」。
繼承的英文原文 inherit ,中文意思泛指從什麼得到什麼,生物學上的遺傳也是用這個詞。
至於封裝的意思就是把資料 (data) 封在類別中,這還牽涉到程式設計中另一個重要的概念 資訊隱藏 (information hiding) ,主要就是不讓外界隨意存取類別的資料,也就是說,只讓類別的資料成員 (data member) 給同個類別的成員函數 (member function) 存取。
這就要用到 private 存取標籤 (access label) 了,就是把成員變數放在 private 之後,而其他可供外界存取的成員函數放在 public 之後
class Demo { | |
// 宣告 public 的成員 | |
public: | |
void set_a(int n); | |
void set_b(int n); | |
int get_a(); | |
int get_b(); | |
int DoSomething(); | |
// 宣告 private 的成員 | |
private: | |
int a; | |
int b; | |
}; |
這裡 a 與 b 已經改放到 private 之後,也由於 a 與 b 都是 private 的,因此另外宣告 public 的 set_a() 與 set_b() 設定 a 與 b 之值, get_a() 與 get_b() 取得 a 與 b 之值。
存取標籤後面要接一個冒號,之後的成員依縮排方式加入。
set_a() 與 set_b() 為修改函數 (mutator) ,就是俗稱的 setter ,至於 get_a() 與 get_b() 為存取函數 (accessor) ,也就是是俗稱的 getter 。
因此 set_a() 、 set_b() 、 get_a() 、 get_b() 的實作很簡單,如下
// setter 與 getter 成員函數 | |
void Demo::set_a(int n) { | |
a = n; | |
} | |
void Demo::set_b(int n) { | |
b = n; | |
} | |
int Demo::get_a() { | |
return a; | |
} | |
int Demo::get_b() { | |
return b; | |
} |
我們寫成一個完整範例,如下
001 | #include <iostream> |
002 | |
003 | using namespace std; |
004 | |
005 | class Demo { |
006 | // 宣告 public 的成員 |
007 | public: |
008 | void set_a(int n); |
009 | void set_b(int n); |
010 | int get_a(); |
011 | int get_b(); |
012 | int DoSomething(); |
013 | |
014 | // 宣告 private 的成員 |
015 | private: |
016 | int a; |
017 | int b; |
018 | }; |
019 | |
020 | int Demo::DoSomething() { |
021 | // 改成呼叫 getter 成員函數 |
022 | return get_a() + get_b(); |
023 | } |
024 | |
025 | // setter 與 getter 成員函數 |
026 | void Demo::set_a(int n) { |
027 | a = n; |
028 | } |
029 | |
030 | void Demo::set_b(int n) { |
031 | b = n; |
032 | } |
033 | |
034 | int Demo::get_a() { |
035 | return a; |
036 | } |
037 | |
038 | int Demo::get_b() { |
039 | return b; |
040 | } |
041 | |
042 | int main(void) { |
043 | Demo t; |
044 | // 由呼叫 setter 設定成員變數 |
045 | t.set_a(11); |
046 | t.set_b(22); |
047 | |
048 | cout << endl; |
049 | cout << t.DoSomething() << endl; |
050 | cout << endl; |
051 | |
052 | return 0; |
053 | } |
054 | |
055 | /* 檔名: class_demo2.cpp |
056 | 作者: Kaiching Chang |
057 | 時間: 2014-5 */ |
編譯執行結果如下
但是這樣設定成員變數還得額外呼叫 set_a() 與 set_b() ,有點麻煩,我們希望宣告 (declare) 時就能夠直接設定,嗯,這用建構函數 (constructor) 就可以囉!
中英文術語對照
物件導向程式設計 | object-oriented programming |
封裝 | encapsulation |
繼承 | inheritance |
多型 | polymorphism |
類別 | class |
子類別 | subclass |
父類別 | superclass |
成員 | member |
資料 | data |
資訊隱藏 | information hiding |
資料成員 | data member |
成員函數 | member function |
存取標籤 | access label |
修改函數 | mutator |
存取函數 | accessor |
宣告 | declare |
建構函數 | constructor |
重點整理
- 物件導向程式設計有封裝、繼承及多型等三大基本特性。
- 繼承像是親屬的重直關係(父母子女),多型則像是親屬的平行關係(兄弟姊妹)。
- 封裝連帶的觀念就是資訊隱藏,類別的成員變數只能在類別中處理,因此要把資料成員宣告在 private 存取標籤下。
- 外界要存取 private 的資料成員要透過 public 的存取函數與修改函數。
問題與討論
- 物件導向程式設計有哪三大基本特性?
- 什麼是封裝?為什麼要做封裝?
- 什麼是繼承?繼承機制有什麼優點?
- 什麼是多型?可以用什麼方式比擬嗎?
- 什麼是存取函數與修改函數?為什麼要有存取函數與修改函數?
練習
- 寫一個程式 exercise1101.cpp ,利用 exercise1005.cpp 設計的 IntegerDemo ,替 value 加入存取函數與修改函數。
- 寫一個程式 exercise1102.cpp ,利用 exercise1006.cpp 計算階層值的類別,替 value 加入存取函數與修改函數。
- 寫一個程式 exercise1103.cpp ,利用 exercise1007.cpp 計算費氏數列的類別,替 value 加入存取函數與修改函數。
the end
沒有留言:
張貼留言