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