C++ 入門指南 4.01

單元 11 - 物件導向與封裝

-unit11-

物件導向程式設計 (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;
};

這裡 ab 已經改放到 private 之後,也由於 ab 都是 private 的,因此另外宣告 publicset_a()set_b() 設定 ab 之值, get_a()get_b() 取得 ab 之值。

存取標籤後面要接一個冒號,之後的成員依縮排方式加入。

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;
}

C++ 可以用關鍵字 (keyword) this 加上 -> 存取成員變數,例如 this->a 就是成員變數 a ,這樣的好處是可以使用跟成員變數相同識別字的參數。

我們寫成一個完整範例,如下

#include <iostream>

using namespace std;

// 宣告類別
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;
};

// 實作 DoSomething() 成員函數
int Demo::DoSomething() {
    // 改成呼叫 getter 成員函數
    return 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;
}

// 程式執行的 main() 函數
int main(void) {
    // 宣告 Demo 型態的物件
    Demo t;
    // 由呼叫 setter 設定成員變數
    t.set_a(12);
    t.set_b(23);

    cout << endl << t.DoSomething() << endl << endl;

    return 0;
}

/* 《程式語言教學誌》的範例程式
   http://kaiching.org/
   檔名:class_demo2.cxx
   功能:示範定義類別
   作者:張凱慶 */

編譯執行結果如下

-classdemo2-

但是這樣設定成員變數還得額外呼叫 set_a()set_b() ,有點麻煩,我們希望宣告 (declare) 時就能夠直接設定,其實這用建構函數 (constructor) 就可以囉!

中英文術語對照
存取標籤access label
存取函數accessor
類別class
建構函數constructor
資料data
資料成員data member
宣告declare
封裝encapsulation
資訊隱藏information hiding
繼承inheritance
關鍵字keyword
成員member
成員函數member function
修改函數mutator
物件導向程式設計object-oriented programming
多型polymorphism
子類別subclass
父類別superclass
重點整理
1. 物件導向程式設計有封裝、繼承及多型等三大基本特性。
2. 繼承像是親屬的重直關係(父母子女),多型則像是親屬的平行關係(兄弟姊妹)。
3. 封裝連帶的觀念就是資訊隱藏,類別的成員變數只能在類別中處理,因此要把資料成員宣告在 private 存取標籤下。
4. 外界要存取 private 的資料成員要透過 public 的存取函數與修改函數。
問題與討論
1. 物件導向程式設計有哪三大基本特性?
2. 什麼是封裝?為什麼要做封裝?
3. 什麼是繼承?繼承機制有什麼優點?
4. 什麼是多型?可以用什麼方式比擬嗎?
5. 什麼是存取函數與修改函數?為什麼要有存取函數與修改函數?
練習
1. 寫一個程式 exercise1101.cxx ,將上述 class_demo2.cxx 裡頭的成員變數加上 this-> ,並把修改函數的參數識別字改成跟成員變數相同。 參考程式碼
2. 寫一個程式 exercise1102.cxx ,利用 exercise1006.cxx 設計的 IntegerDemo ,將 value 宣告在 private 標籤下,替 value 加入存取函數與修改函數。 參考程式碼
3. 寫一個程式 exercise1103.cxx ,利用 exercise1007.cxx 計算階層值的類別,將 value 宣告在 private 標籤下,替 value 加入存取函數與修改函數。 參考程式碼
4. 寫一個程式 exercise1104.cxx ,利用 exercise1008.cxx 計算費氏數列的類別,將 value 宣告在 private 標籤下,替 value 加入存取函數與修改函數。 參考程式碼
5. 寫一個程式 exercise1105.cxx ,利用 exercise1009.cxxPoint 類別,將 xy 宣告在 private 標籤下,繼續替 xy 加入存取函數與修改函數。 參考程式碼
6. 寫一個程式 exercise1106.cxx ,利用 exercise1010.cxxGuess 類別,將 answer 宣告在 private 標籤下,繼續替 answer 加入存取函數與修改函數。 參考程式碼
7. 寫一個程式 exercise1107.cxx ,利用 exercise1011.cxxMember 類別,將 idname 宣告在 private 標籤下,繼續替 idname 加入存取函數與修改函數。 參考程式碼
8. 寫一個程式 exercise1108.cxx ,利用 exercise1012.cxxText 類別,將 content 宣告在 private 標籤下,繼續替 content 加入存取函數與修改函數。 參考程式碼
9. 寫一個程式 exercise1109.cxx ,利用 exercise1013.cxxBall 類別,將 p 宣告在 private 標籤下,繼續替 p 加入存取函數與修改函數,其中存取函數改成回傳座標的字串。 參考程式碼
10. 寫一個程式 exercise1110.cxx ,利用 exercise1014.cxxScene 類別,將 idtext 宣告在 private 標籤下,繼續替 idtext 加入存取函數與修改函數。 參考程式碼
11. 寫一個程式 exercise1111.cxx ,利用 exercise1015.cxxGame 類別,將 guess 宣告在 private 標籤下,繼續替 guess 加入存取函數與修改函數,其中存取函數直接回傳 guessanswer 成員。 參考程式碼

相關教學影片

上一頁 單元 10 - 類別
回 C++ 入門指南 4.01 目錄
下一頁 單元 12 - 建構函數
回 C++ 教材
回程式語言教材首頁