HOME ABOUT CONTACT

C/C++教學: 第二十八課 - 類別(Class)的繼承(Inheritance)

Rain December 14, 2024
Outline

1. 簡介

2. 繼承(Inheritance)

3. 多重繼承

4. 繼承與封裝關係

簡介top

接下來介紹設計類別的核心概念 - 繼承(Inheritance)。繼承的特性讓類別有很好的拓展性和更好的架構分類,通常在設計程式架構時,會先從主題的最基本元件開始搭建,例如我們要做一個公司員工的資料架構時,可以先設計一個基礎類別為 "員工",然後再從這個 "員工" 類別去衍生出 "基層員工"、"部門主管"、"高階主管" 等等的子類別。

因為子類別繼承了 "員工" 類別,它們之間就形成了父類別和子類別的關係。像這樣的設計,"員工" 類別會具有子類別的共通方法和屬性,例如 "員工" 都會有員工編號、薪資,因此不管是 "基層員工"、"部門主管" 和 "高階主管" 類別都會有這樣的基礎屬性,而這些子類別在基於這樣的基礎上,會被各自設計成自己獨有的方法和屬性,你可以再用 "權力" 這樣的思維方向去考慮如何設計這些子類別。

繼承(Inheritance)top

那麼,我們先來看看類別繼承的語法該如何撰寫:


class Parent {

};

class Child : public Parent {

};
                        

我們先定義了一個類別叫 Parent,然後再定義一個類別 Child 去繼承 Parent,語法上只要在定義類別 Child 之後用 "冒號(:)" 加上父類別名稱即可。你可能會有疑問為何在繼承父類別之前,要再加上 public 呢?

這意思是子類別是以公開存取的方式繼承了父類別,這樣我們要調用父類別 public 的方法時,就可以直接透過子類別去調用,我們來看看下面範例:


class Parent {
public:
    void print() {
        std::cout << "Hello, I'm parent" << std::endl;
    }
};

class Child : public Parent {

};

int main () {
    Child c;
    c.print();
}
                        

如果在繼承 Parent 類別時,把 public 拿掉,你會發現 Child 類別不能直接用 print 去輸出顯示了,並且在編譯時會報錯誤。你必須得用 Child 自身的方法去調用 Parent 的 print(),如下範例:


class Parent {
public:
    void print() {
        std::cout << "Hello, I'm parent" << std::endl;
    }
};

class Child : Parent {
    void call_parent_print() {
        print();
    }
};

int main () {
    Child c;
    c.call_parent_print();
}
                        

多重繼承top

在前述論及繼承中,我們談論的較偏向類別的深度集合(衍生),而多重繼承則是讓繼承擁有廣度集合(衍生)的特性,這意味著子類別可以不只繼承單一個父類別。

回到前例,假設我們想在員工架構上在建立一個基礎,這個類別可以叫 "工程師",然後這個 "工程師" 類別再衍生出 "軟體工程師" 和 "硬體工程師",這樣我們對員工的分類又更進一個層次分類,當我們想建立一個 "軟體工程師部門主管" 的類別時,就可以讓這個類別去繼承 "軟體工程師" 和 "部門主管"。

那麼,我們要如何撰寫讓一個類別繼承多個類別呢? 可以有幾種寫法,如下範例:


class Employee {
public:
    void print1() {
        std::cout << "Hello, I'm a employee." << std::endl;
    }
};
                                
class Engineer {
public:
    void print2() {
        std::cout << "Hello, I'm a engineer." << std::endl;
    }
};
                                
class Child : public Employee, public Engineer {
                                
};
                        

class Employee {
public:
    void print1() {
        std::cout << "Hello, I'm a employee." << std::endl;
    }
};
                                
class Engineer : public Employee {
public:
    void print2() {
        std::cout << "Hello, I'm a engineer." << std::endl;
    }
};
                                
class Child : public Engineer {
                                
};
                        

值得注意的是,父類別彼此之間要避免有一樣名稱的函式,否則編譯時可能會出現錯誤,因為如果子類別有調用該函式,會變得不曉得呼叫的是哪個父類別的函式。

繼承與封裝關係top

還記得上一篇談論封裝時,我們有提到 protected 修飾詞底下的方法和屬性是只有自身和子類別可以存取的吧? 那麼它在工程意義上是什麼意思呢? 假設我們有一個屬性名叫 "m_salary",我們想讓這個屬性可以在所有的衍生類別中被存取,但又不希望它是公開可以隨意被更改的,這時候就可以用 protected,如下範例:


class Employee {
public:
    void print1() {
        std::cout << "Hello, I'm a employee." << std::endl;
    }
protected:
    int m_salary;
};
                                
class Engineer {
public:
    void print2() {
        std::cout << "Hello, I'm a engineer." << std::endl;
    }
};
                                
class Child : public Employee, public Engineer {
public:
    void set_salary(int s) {
        m_salary = s;
    }
    int get_salary() {
        return m_salary;
    }
};
                                
int main()
{
    Child c;
    c.print1();
    c.print2();
    c.set_salary(100000);
    std::cout << c.get_salary() << std::endl;
}
                        

下一篇: 第二十九課 - 類別(Class)的多型(Polymorphism)


Last updated:

Related Article List

  1. C/C++教學: 第二十六課 - 基於範圍的 for 迴圈(Range-based For Loop)
  2. C/C++教學: 第二十七課 - 類別(Class)的封裝(Encapsulation)
  3. C/C++教學: 第二十九課 - 類別(Class)的多形(Polymorphism)