HOME ABOUT CONTACT

C/C++教學: 第二十九課 - 類別(Class)的多型(Polymorphism)

Rain December 21, 2024
Outline

1. 簡介

2. 虛擬(Virtual)和覆蓋(Override)

3. 轉變(Transform)

簡介top

類別多型(Polymorphism)是多數人難以理解的環節,然而在大多數面試中又很常被拿出來考驗面試者的題目。那麼什麼是多型呢? 簡單來說,就是一個介面提供給多種不同類型的物件,進行對應不同的操作。

多型(Polymorphism)可以說是繼承(Inheritance)概念的延伸,它大致包含了覆蓋(Override)和轉變(Transform)兩個環節,我想沿用上一篇繼承的例子來讓你方便理解。

虛擬(Virtual)和覆蓋(Override)top

說到覆蓋(Override,或稱覆寫),可能常看到的修飾詞是 virtual,它會用來修飾類別中公開的方法(函式),它意味著子類別"可能"會覆寫掉這個函式,因此可能需要查看子類別的定義域是否有重新覆寫這個函式。

然而,不管有無使用 virtual 修飾公開的函式,子類別都可以覆寫,virtual 這個修飾詞可以標明給開發者知道,由這個類別衍生出來的子類別,可能會有覆寫此函式的可能性。我們來看看下面例子:


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

class Child : public Parent {
public:
	void print() override //override 用以標明覆寫父類別的虛擬函式
	{
		std::cout << "Hello, I'm a child." << std::endl;
	}
};
                        

回到前述,我特別表明 virtual 是告訴開發者,這個公開函式"可能"會被覆寫,因為子類別確實是不一定要覆寫這個被 virtual 修飾的函式,只有一種情形是子類別一定要覆寫的,那就是純虛函式

什麼是純虛函式呢? 它代表著父類別僅僅只有定義這個函式,而沒有實作層(實際作為的程式碼),所有繼承這個父類別的子類別,都必須去實現這個實作層。看看下例:


class Parent {
public:
    virtual void print() = 0; //定義為純虛函式
};
                                
class Child : public Parent {
public:
    void print() override //子類別必須實作
    {
        std::cout << "Hello, I'm a child." << std::endl;
    }
};
                        

轉變(Transform)top

如果我們要設計一個函式,並且這個函式的參數是某個類別,但這個類別跟別的類別是繼承關係,該怎麼設計這個函式會比較好呢?

像這樣的情況,我們可以將參數定義為該類別的父類別作為參數,當需要調用到子類別的方法或屬性時,我們可以再進行轉換即可。這樣的函式就形成了一個的介面,但可處理該類別和其衍生類別的操作處理,我們來看看下例:


class Parent {
public:
    virtual void print() {
        std::cout << "Hello, I'm the parent." << std::endl;
    }
};
                                
class Child : public Parent {
public:
    void print() override 
    {
        std::cout << "Hello, I'm a child." << std::endl;
    }
};

void func(class Parent *p) { //定義參數為 Parent 類別
    p->print(); // 調用的會是 Child 的方法,因為實際輸入的是 Child
}

void main() {
    class Child c;
    func(&c); //類別 Child 可作為參數,因為 Child 繼承了 Parent
}
                        

上例中,我們是直接在主函式中給予了 Child 類別讓函式去執行,函式中調用的就會是 Child 的方法(print)。那假如我們在主函式中拿到的是 Parent,但想給函式調用的是 Child 的方法,可以怎麼做呢?


class Parent {
public:
    virtual void print() {
        std::cout << "Hello, I'm the parent." << std::endl;
    }
};
                                
class Child : public Parent {
public:
    void print() override 
    {
        std::cout << "Hello, I'm a child." << std::endl;
    }
};

void func(class Parent *p) { //定義參數為 Parent 類別
    class Child *c = static_cast< class Child*>(p); //使用 static_cast 將 Parent 轉成 Child 
    c->print();
}

void main() {
    class Parent p;
    func(&p); //類別 Child 可作為參數,因為 Child 繼承了 Parent
}
                        

下一篇: 第三十課 - 類別(Class)的建構(Constructor)和解構(Destructor)


Last updated:

Related Article List

  1. C/C++教學: 第二十七課 - 類別(Class)的封裝(Encapsulation)
  2. C/C++教學: 第二十八課 - 類別(Class)的繼承(Inheritance)
  3. C/C++教學: 第三十課 - 類別(Class)的建構(Constructor)和解構(Destructor)