多態是面向對象編程的三大特性之一,C++提供了兩種主要的多態形式:動態多態和靜態多態。本文將詳細解釋它們的區別,並通過代碼示例進行説明。

什麼是多態?

多態(Polymorphism)指同一個接口可以表現出不同的行為。在C++中,這允許我們使用統一的接口來處理不同類型的對象。

動態多態(運行時多態)

動態多態在程序運行時確定調用哪個函數,主要通過虛函數和繼承機制實現。

實現機制

  • 使用虛函數(virtual function)
  • 通過繼承關係
  • 運行時通過虛函數表(vtable)決定調用哪個函數

代碼示例

#include <iostream>
using namespace std;

// 基類
class Animal {
public:
    // 虛函數
    virtual void makeSound() {
        cout << "Animal makes a sound" << endl;
    }
    
    virtual ~Animal() = default; // 虛析構函數
};

// 派生類
class Dog : public Animal {
public:
    void makeSound() override {
        cout << "Dog barks: Woof! Woof!" << endl;
    }
};

class Cat : public Animal {
public:
    void makeSound() override {
        cout << "Cat meows: Meow! Meow!" << endl;
    }
};

// 使用動態多態
void animalSound(Animal* animal) {
    animal->makeSound(); // 運行時決定調用哪個makeSound
}

int main() {
    Dog dog;
    Cat cat;
    Animal animal;
    
    // 通過基類指針調用,表現出多態行為
    Animal* animals[] = {&animal, &dog, &cat};
    
    for (auto* animal : animals) {
        animalSound(animal);
    }
    
    return 0;
}

輸出:

Animal makes a sound
Dog barks: Woof! Woof!
Cat meows: Meow! Meow!

動態多態特點

  • 運行時綁定:函數調用在運行時決定
  • 靈活性高:可以在運行時改變行為
  • 性能開銷:有虛函數表查找的開銷
  • 必須使用指針或引用:通過基類指針或引用調用

靜態多態(編譯時多態)

靜態多態在編譯時確定調用哪個函數,主要通過函數重載和模板實現。

1. 函數重載

#include <iostream>
using namespace std;

class Calculator {
public:
    // 函數重載 - 靜態多態
    int add(int a, int b) {
        return a + b;
    }
    
    double add(double a, double b) {
        return a + b;
    }
    
    string add(const string& a, const string& b) {
        return a + b;
    }
};

int main() {
    Calculator calc;
    
    cout << "Int addition: " << calc.add(5, 3) << endl;
    cout << "Double addition: " << calc.add(5.5, 3.3) << endl;
    cout << "String addition: " << calc.add("Hello, ", "World!") << endl;
    
    return 0;
}

輸出:

Int addition: 8
Double addition: 8.8
String addition: Hello, World!

2. 模板(泛型編程)

#include <iostream>
#include <vector>
#include <list>
using namespace std;

// 函數模板 - 靜態多態
template<typename T>
T multiply(T a, T b) {
    return a * b;
}

// 類模板
template<typename Container>
void printContainer(const Container& container) {
    for (const auto& item : container) {
        cout << item << " ";
    }
    cout << endl;
}

// 特化示例
template<>
string multiply<string>(string a, string b) {
    return "String multiplication not supported";
}

int main() {
    // 模板函數使用
    cout << "Int multiplication: " << multiply(5, 3) << endl;
    cout << "Double multiplication: " << multiply(5.5, 2.0) << endl;
    cout << "String multiplication: " << multiply<string>("hello", "world") << endl;
    
    // 模板類使用
    vector<int> vec = {1, 2, 3, 4, 5};
    list<string> lst = {"apple", "banana", "cherry"};
    
    cout << "Vector: ";
    printContainer(vec);
    
    cout << "List: ";
    printContainer(lst);
    
    return 0;
}

輸出:

Int multiplication: 15
Double multiplication: 11
String multiplication: String multiplication not supported
Vector: 1 2 3 4 5 
List: apple banana cherry 

3. CRTP(奇異遞歸模板模式)

#include <iostream>
using namespace std;

// CRTP基類
template<typename Derived>
class AnimalBase {
public:
    void makeSound() {
        static_cast<Derived*>(this)->makeSoundImpl();
    }
};

// 派生類
class Dog : public AnimalBase<Dog> {
public:
    void makeSoundImpl() {
        cout << "Dog barks: Woof! Woof!" << endl;
    }
};

class Cat : public AnimalBase<Cat> {
public:
    void makeSoundImpl() {
        cout << "Cat meows: Meow! Meow!" << endl;
    }
};

// 使用靜態多態
template<typename T>
void animalSound(AnimalBase<T>& animal) {
    animal.makeSound(); // 編譯時決定調用哪個函數
}

int main() {
    Dog dog;
    Cat cat;
    
    animalSound(dog);
    animalSound(cat);
    
    return 0;
}

輸出:

Dog barks: Woof! Woof!
Cat meows: Meow! Meow!

動態多態 vs 靜態多態

特性 動態多態 靜態多態
綁定時間 運行時 編譯時
實現機制 虛函數、繼承 模板、函數重載
性能 有運行時開銷(虛表查找) 無運行時開銷
靈活性 運行時決定行為 編譯時決定行為
二進制大小 較小 可能較大(模板實例化)
調試難度 相對容易 相對困難
使用場景 需要運行時動態行為 性能要求高,類型已知

實際應用建議

使用動態多態的場景:

  • 需要在運行時決定對象類型
  • 有複雜的繼承層次結構
  • 需要插件架構或動態加載
  • 代碼可讀性和維護性更重要

使用靜態多態的場景:

  • 性能是關鍵因素
  • 類型在編譯時已知
  • 需要避免虛函數開銷
  • 使用模板元編程

總結

C++中的多態提供了強大的代碼複用和靈活性:

  • 動態多態通過虛函數提供運行時靈活性,適合需要動態行為變化的場景
  • 靜態多態通過模板和重載提供零開銷的抽象,適合性能敏感的場景

在實際開發中,應根據具體需求選擇合適的多態方式,有時甚至可以結合使用兩者以獲得最佳效果。理解這兩種多態的區別和適用場景,有助於編寫更高效、更靈活的C++代碼。