繼承是面向對象編程的三大特性之一,也是C中實現代碼復用和多態的重要機制。本文將帶你深入理解C繼承的核心概念與應用。
一、繼承的基本概念
1.1 什么是繼承?
繼承允許我們基于已有的類創建新類,新類(派生類)可以繼承已有類(基類)的屬性和方法,并添加自己的特性。
繼承的優勢:
-
代碼復用:避免重復編寫相同代碼
-
擴展性:在現有類基礎上添加新功能
-
層次結構:建立類之間的層次關系
1.2 繼承語法
class BaseClass {// 基類成員
};class DerivedClass : access-specifier BaseClass {// 派生類成員
};
訪問說明符:
-
public
:基類的公有成員在派生類中保持公有 -
protected
:基類的公有成員在派生類中變為保護 -
private
:基類的公有和保護成員在派生類中變為私有(默認)
二、繼承類型詳解
2.1 公有繼承(public)
最常用的繼承方式,遵循"is-a"關系
class Animal {
public:void eat() { cout << "Eating..." << endl; }
protected:int age;
};class Dog : public Animal {
public:void bark() { eat(); // 可訪問基類public方法age = 2; // 可訪問基類protected成員cout << "Woof!" << endl; }
};int main() {Dog myDog;myDog.eat(); // 輸出: Eating...myDog.bark(); // 輸出: Woof!
}
2.2 保護繼承(protected)
基類的公有和保護成員在派生類中都變為保護
class Vehicle {
public:void start() { cout << "Starting..." << endl; }
};class Car : protected Vehicle {
public:void drive() { start(); // 在派生類中可以訪問}
};int main() {Car myCar;// myCar.start(); // 錯誤! 在類外不可訪問myCar.drive(); // 正確
}
2.3 私有繼承(private)
基類的所有成員在派生類中都變為私有(默認繼承方式)
class Engine {
public:void ignite() { cout << "Igniting..." << endl; }
};class Car : private Engine { // private可省略
public:void start() { ignite(); // 在派生類中可以訪問}
};int main() {Car myCar;// myCar.ignite(); // 錯誤! 在類外不可訪問myCar.start(); // 正確
}
三、派生類的構造與析構
3.1 構造順序
-
基類構造函數
-
派生類的成員對象構造函數
-
派生類自身構造函數
3.2 析構順序
與構造順序相反:
-
派生類自身析構函數
-
派生類的成員對象析構函數
-
基類析構函數
class Base {
public:Base() { cout << "Base constructor" << endl; }~Base() { cout << "Base destructor" << endl; }
};class Derived : public Base {
public:Derived() : Base() { cout << "Derived constructor" << endl; }~Derived() { cout << "Derived destructor" << endl; }
};int main() {Derived d;/* 輸出:Base constructorDerived constructorDerived destructorBase destructor*/
}
四、函數重寫與多態性
4.1 虛函數(virtual)
使用virtual
關鍵字聲明可在派生類中重寫的函數
class Shape {
public:virtual void draw() {cout << "Drawing a shape" << endl;}
};class Circle : public Shape {
public:void draw() override { // C++11引入override關鍵字cout << "Drawing a circle" << endl;}
};int main() {Shape* shape = new Circle();shape->draw(); // 輸出: Drawing a circledelete shape;
}
4.2 純虛函數與抽象類
包含純虛函數的類稱為抽象類,不能實例化
class Shape {
public:virtual void draw() = 0; // 純虛函數
};class Rectangle : public Shape {
public:void draw() override {cout << "Drawing a rectangle" << endl;}
};int main() {// Shape s; // 錯誤! 抽象類不能實例化Shape* shape = new Rectangle();shape->draw(); // 輸出: Drawing a rectangledelete shape;
}
五、多重繼承與虛繼承
5.1 多重繼承
一個類可以繼承多個基類
class Printer {
public:void print() { cout << "Printing..." << endl; }
};class Scanner {
public:void scan() { cout << "Scanning..." << endl; }
};class Copier : public Printer, public Scanner {
public:void copy() {print();scan();cout << "Copying completed!" << endl;}
};
5.2 菱形繼承問題
菱形繼承結構:
Animal/ \/ \
Mammal WingedAnimal\ /\ /Bat
問題描述:
當 Bat 類同時繼承 Mammal 和 WingedAnimal 時,如果它們都繼承自 Animal,會導致 Bat 對象中包含兩份 Animal 成員
class Animal {
public:int age;
};class Mammal : public Animal {};
class WingedAnimal : public Animal {};class Bat : public Mammal, public WingedAnimal {};int main() {Bat bat;// bat.age = 2; // 錯誤! 二義性訪問bat.Mammal::age = 2; // 通過Mammal路徑訪問bat.WingedAnimal::age = 3; // 通過WingedAnimal路徑訪問cout << bat.Mammal::age << endl; // 輸出: 2cout << bat.WingedAnimal::age << endl; // 輸出: 3
}
5.3 虛繼承解決方案
使用virtual
關鍵字解決菱形繼承的二義性問題
class Animal {
public:int age;
};class Mammal : virtual public Animal {};
class WingedAnimal : virtual public Animal {};class Bat : public Mammal, public WingedAnimal {};int main() {Bat bat;bat.age = 2; // 沒有二義性// 未使用虛繼承時: bat.Mammal::age 和 bat.WingedAnimal::age
}
六、繼承中的訪問控制總結
基類成員訪問權限 | 繼承類型 | 在派生類中的訪問權限 |
public | public | public |
protected | public | protected |
private | public | 不可訪問 |
public | protected | protected |
protected | protected | protected |
private | protected | 不可訪問 |
public | private | private |
protected | private | private |
private | private | 不可訪問 |
七、繼承的最佳實踐
-
遵循LSP原則:派生類應該能夠完全替代基類
-
優先使用組合:除非是"is-a"關系,否則優先使用組合而非繼承
-
避免深層次繼承:繼承層次不宜過深(通常不超過3層)
-
使用override關鍵字:明確表示重寫基類虛函數
-
基類析構函數聲明為虛函數:確保正確調用派生類析構函數
class Base {
public:virtual ~Base() {} // 虛析構函數
};
八、總結
C++繼承機制提供了強大的代碼復用和多態支持:
-
三種繼承方式:public、protected、private
-
虛函數實現運行時多態
-
虛繼承解決菱形繼承問題
-
抽象類定義接口規范
正確使用繼承可以使代碼更加靈活、可擴展,但需注意避免過度使用繼承帶來的設計問題。
掌握繼承機制是成為C++高級開發者的必經之路。希望本文能幫助你建立清晰的繼承概念體系,在實際開發中靈活運用這些知識!