類別多型(Polymorphism)是多數人難以理解的環節,然而在大多數面試中又很常被拿出來考驗面試者的題目。那麼什麼是多型呢? 簡單來說,就是一個介面提供給多種不同類型的物件,進行對應不同的操作。
多型(Polymorphism)可以說是繼承(Inheritance)概念的延伸,它大致包含了覆蓋(Override)和轉變(Transform)兩個環節,我想沿用上一篇繼承的例子來讓你方便理解。
說到覆蓋(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;
}
};
如果我們要設計一個函式,並且這個函式的參數是某個類別,但這個類別跟別的類別是繼承關係,該怎麼設計這個函式會比較好呢?
像這樣的情況,我們可以將參數定義為該類別的父類別作為參數,當需要調用到子類別的方法或屬性時,我們可以再進行轉換即可。這樣的函式就形成了一個的介面,但可處理該類別和其衍生類別的操作處理,我們來看看下例:
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: