目錄
摘要
虛函數(Virtual Functions)
定義
用法
純虛函數(Pure Virtual Functions)
定義
用法
需要避開的坑
總結
摘要
在C++中,我們經常會在開發中使用到虛函數(Virtual Functions)和純虛函數(Pure Virtual Functions),而它們都是面向對象編程的重要概念,它們允許實現多態性,但也有一些我們在實際編程時需要注意的坑。
虛函數(Virtual Functions)
定義
虛函數是在基類中聲明并且使用 `virtual` 關鍵字修飾的成員函數。它可以被派生類重寫,以實現運行時多態性。
class Base {
public:virtual void show() {std::cout << "Base class show function" << std::endl;}
};
用法
1. 基類中聲明虛函數:
? ?在基類中聲明虛函數,以便派生類可以重寫它。
class Base {
public:virtual void show() {std::cout << "Base class show function" << std::endl;}
};
2. 派生類中重寫虛函數:
? ?在派生類中使用相同的函數簽名重寫虛函數。
? ?
class Derived : public Base {
public:void show() override {std::cout << "Derived class show function" << std::endl;}
};
3. 使用基類指針或引用調用虛函數:
? ?通過基類指針或引用調用虛函數時,會根據對象的實際類型調用相應的函數。
void describe(const Base &obj) {obj.show();
}int main() {Base baseObj;Derived derivedObj;describe(baseObj); // 輸出 "Base class show function"describe(derivedObj); // 輸出 "Derived class show function"return 0;
}
純虛函數(Pure Virtual Functions)
定義
純虛函數是在基類中聲明但沒有實現的虛函數,通過在函數聲明末尾使用 `= 0` 來聲明。
class AbstractBase {
public:virtual void show() = 0; // 純虛函數
};
用法
1. 作為接口:
? ?純虛函數通常用于定義接口,要求派生類必須提供實現。
class AbstractBase {
public:virtual void show() = 0; // 純虛函數,定義接口
};class ConcreteDerived : public AbstractBase {
public:void show() override {std::cout << "ConcreteDerived class show function" << std::endl;}
};
2. 抽象基類:
? ?包含純虛函數的類稱為抽象基類,不能實例化,只能用作派生類的接口。
AbstractBase *ptr = new ConcreteDerived();
ptr->show(); // 輸出 "ConcreteDerived class show function"
需要避開的坑
1. 未實現純虛函數的派生類:
? ?如果派生類沒有實現基類中的所有純虛函數,則該派生類仍然是抽象的,不能實例化。
2. 構造函數中調用虛函數:
? ?在構造函數中調用虛函數,會導致基類的版本被調用,而不是派生類的版本。
3. 析構函數中調用虛函數:
? ?在析構函數中調用虛函數,會導致派生類的版本被調用,但是可能會出現問題,因為在派生類對象的析構函數執行完畢后,其成員變量被銷毀,再調用虛函數就可能會訪問到已銷毀的內存,導致未定義行為。
4. 虛函數的默認參數:
? ?虛函數的默認參數是靜態綁定的,不會根據運行時對象的實際類型而改變。
5. 虛函數與模板:
? ?在模板類中使用虛函數可能會導致編譯器難以生成有效的代碼,因為模板的特化版本可能會對虛函數的實現產生影響。
#include <iostream>class AbstractBase {
public:virtual void show() = 0; // 純虛函數virtual ~AbstractBase() {}
};class ConcreteDerived : public AbstractBase {
public:void show() override {std::cout << "ConcreteDerived class show function" << std::endl;}~ConcreteDerived() {std::cout << "ConcreteDerived destructor" << std::endl;}
};int main() {// AbstractBase *ptr = new AbstractBase(); // 錯誤:不能實例化抽象類AbstractBase *ptr = new ConcreteDerived();ptr->show(); // 輸出 "ConcreteDerived class show function"delete ptr; // 輸出 "ConcreteDerived destructor"return 0;
}
總結
1. 虛函數:
? - 使用 `virtual` 關鍵字聲明,允許在派生類中重寫。
? - 允許通過基類指針或引用調用派生類的方法。
2. 純虛函數:
? - 使用 `virtual` 關鍵字聲明并在末尾添加 `= 0`,作為接口的一部分。
? - 不能實例化,只能作為派生類的接口,派生類必須實現所有純虛函數。
虛函數和純虛函數在實際應用中廣泛使用,且很多功能的繼承和多態也離不開虛函數。需要我們注意到的是虛函數留下的坑,不然問題真的很難被發現。