一、继承机制:代码复用的基石

1.1 继承的概念与本质

继承是面向对象编程中实现代码复用的核心机制,它允许一个类(派生类)获取另一个类(基类)的成员变量和成员函数。这种关系类似于现实世界中的 "is-a" 关系,例如 "狗是一种动物"。

在 C++ 中,继承通过类定义时的冒号语法实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Animal {
public:
void speak() {
std::cout << "The animal makes a sound." << std::endl;
}
};

class Dog : public Animal {
public:
void bark() {
std::cout << "The dog barks." << std::endl;
}
};

在上述示例中,Dog 类继承自 Animal 类,Dog 类的对象不仅可以调用自己的 bark 函数,还能调用从 Animal 类继承而来的 speak 函数。继承的本质是创建新的数据类型,该类型自动包含基类的特性,并可添加新特性或修改已有特性。

1.2 访问控制与继承方式

C++ 提供三种访问控制符,用于控制基类成员在派生类中的可见性:

  • public(公有):基类的公有成员在派生类中仍为公有

  • protected(保护):基类的保护成员在派生类中仍为保护

  • private(私有):基类的私有成员在派生类中不可直接访问

同时,继承方式也有三种:

  • public 继承:保持基类成员的访问级别

  • protected 继承:基类的 public 成员变为 protected

  • private 继承:基类的 public 和 protected 成员变为 private

以 public 继承为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Base {
public:
int publicMember;
protected:
int protectedMember;
private:
int privateMember;
};

class Derived : public Base {
public:
void accessMembers() {
publicMember = 10; // 合法,基类公有成员在派生类中仍为公有
protectedMember = 20; // 合法,基类保护成员在派生类中仍为保护
// privateMember = 30; // 非法,基类私有成员在派生类中不可直接访问
}
};

1.3 继承层次结构

单继承是指一个派生类仅从一个基类继承,形成简单的线性关系。多继承则允许一个派生类从多个基类继承,能组合多个类的特性,但可能导致 "菱形继承" 问题(同一个基类被间接继承多次)。

解决菱形继承问题的方法是使用虚继承:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class A {
public:
int sharedData;
};

class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {};

int main() {
D d;
d.sharedData = 10; // 通过虚继承,避免二义性,可直接访问
return 0;
}

虚继承确保派生类中只保留一份基类成员的拷贝,避免二义性。

二、多态机制:接口与实现的分离

2.1 多态的概念与分类

多态是指同一接口的不同实现,在 C++ 中主要分为:

  • 编译时多态:通过函数重载和模板实现,在编译阶段确定调用哪个函数

  • 运行时多态:通过虚函数实现,在运行阶段确定调用哪个函数

运行时多态是面向对象编程的核心特性,它允许程序在不了解对象具体类型的情况下,通过基类接口调用正确的派生类实现。

2.2 虚函数与动态绑定

虚函数是在基类中声明为 virtual 的成员函数,派生类可以重写(override)这些函数。当通过基类指针或引用调用虚函数时,会根据对象的实际类型调用相应的版本,这一过程称为动态绑定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Shape {
public:
virtual void draw() {
std::cout << "Drawing a generic shape." << std::endl;
}
};

class Circle : public Shape {
public:
void draw() override {
std::cout << "Drawing a circle." << std::endl;
}
};

class Rectangle : public Shape {
public:
void draw() override {
std::cout << "Drawing a rectangle." << std::endl;
}
};

int main() {
Shape* shapes[2];
shapes[0] = new Circle();
shapes[1] = new Rectangle();

for (int i = 0; i < 2; ++i) {
shapes[i]->draw(); // 根据对象实际类型调用相应的draw函数
delete shapes[i];
}

return 0;
}

override 关键字(C++11 引入)显式指明函数重写基类虚函数,有助于编译器检查错误。

2.3 纯虚函数与抽象类

纯虚函数是没有实现的虚函数,声明方式为在函数原型后加 = 0:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Shape {
public:
virtual void draw() = 0; // 纯虚函数
};

class Circle : public Shape {
public:
void draw() override {
std::cout << "Drawing a circle." << std::endl;
}
};

class Rectangle : public Shape {
public:
void draw() override {
std::cout << "Drawing a rectangle." << std::endl;
}
};

包含纯虚函数的类称为抽象类,抽象类不能实例化,只能作为基类被继承。派生类必须实现所有纯虚函数才能成为可实例化的具体类。

抽象类的主要作用是定义接口,强制派生类实现特定功能,体现 "接口重用" 的设计思想。