继承是面向对象编程的三大特性之一,也是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++高级开发者的必经之路。希望本文能帮助你建立清晰的继承概念体系,在实际开发中灵活运用这些知识!