C/C++ Linux 开发教程(GCC + CMake) / 继承与多态
继承与多态
1. 继承语法与访问控制
C++ 支持三种继承方式,决定了基类成员在派生类中的可见性。
| 基类成员 → 继承方式 | public 继承 | protected 继承 | private 继承 |
|---|---|---|---|
public 成员 | public | protected | private |
protected 成员 | protected | protected | private |
private 成员 | 不可访问 | 不可访问 | 不可访问 |
#include <iostream>
#include <string>
class Animal {
public:
std::string name;
void eat() { std::cout << name << " 在吃东西\n"; }
protected:
int age;
void grow() { ++age; }
private:
int id_; // 派生类无法访问
};
// public 继承:最常用,is-a 关系
class Dog : public Animal {
public:
Dog(const std::string& n, int a) {
name = n; // public → public
age = a; // protected → protected
// id_ = 0; // 错误:private 不可访问
}
void bark() {
grow(); // 可以调用 protected 成员
std::cout << name << " 汪汪叫!\n";
}
};
int main() {
Dog dog("旺财", 3);
dog.eat(); // OK:public 继承保持 public
dog.bark(); // OK
// dog.grow(); // 错误:protected 不可从外部访问
// dog.age = 5; // 错误:protected
return 0;
}
💡 提示:实际开发中 99% 使用
public继承。protected/private继承用于非常少见的实现继承场景。
2. 虚函数(virtual)
虚函数是 C++ 实现运行时多态的核心机制。
#include <iostream>
#include <string>
class Shape {
public:
// 虚函数:派生类可以覆盖
virtual void draw() const {
std::cout << "绘制一个形状\n";
}
// 虚析构函数:确保通过基类指针删除派生类对象时正确析构
virtual ~Shape() = default;
};
class Circle : public Shape {
double radius_;
public:
explicit Circle(double r) : radius_(r) {}
// 覆盖基类虚函数
void draw() const override {
std::cout << "绘制圆形,半径=" << radius_ << "\n";
}
};
class Rectangle : public Shape {
double width_, height_;
public:
Rectangle(double w, double h) : width_(w), height_(h) {}
void draw() const override {
std::cout << "绘制矩形,宽=" << width_ << " 高=" << height_ << "\n";
}
};
int main() {
// 基类指针指向派生类对象 → 多态
Shape* shapes[] = {
new Circle(5.0),
new Rectangle(3.0, 4.0),
new Shape()
};
for (const auto* s : shapes) {
s->draw(); // 运行时调用实际类型的 draw()
}
for (auto* s : shapes) {
delete s; // 虚析构确保正确释放
}
return 0;
}
输出:
绘制圆形,半径=5
绘制矩形,宽=3 高=4
绘制一个形状
⚠️ 注意:如果基类有虚函数,必须将析构函数声明为
virtual,否则通过基类指针删除派生类对象会导致未定义行为(资源泄漏)。
3. 纯虚函数与抽象类
#include <iostream>
#include <string>
#include <vector>
#include <memory>
// 抽象基类:包含纯虚函数,不可实例化
class Drawable {
public:
// 纯虚函数 = 0
virtual void draw() const = 0;
virtual std::string name() const = 0;
virtual double area() const = 0;
// 虚析构函数
virtual ~Drawable() = default;
// 抽象类也可以有非纯虚函数
void describe() const {
std::cout << name() << ": 面积=" << area() << "\n";
}
};
class Circle : public Drawable {
double radius_;
public:
explicit Circle(double r) : radius_(r) {}
void draw() const override {
std::cout << "● 半径=" << radius_ << "\n";
}
std::string name() const override { return "圆形"; }
double area() const override {
return 3.14159265 * radius_ * radius_;
}
};
class Triangle : public Drawable {
double base_, height_;
public:
Triangle(double b, double h) : base_(b), height_(h) {}
void draw() const override {
std::cout << "▲ 底=" << base_ << " 高=" << height_ << "\n";
}
std::string name() const override { return "三角形"; }
double area() const override {
return 0.5 * base_ * height_;
}
};
int main() {
// Drawable d; // 错误:抽象类不可实例化
std::vector<std::unique_ptr<Drawable>> shapes;
shapes.push_back(std::make_unique<Circle>(5.0));
shapes.push_back(std::make_unique<Triangle>(3.0, 4.0));
for (const auto& s : shapes) {
s->draw();
s->describe();
}
return 0;
}
4. override 与 final
| 关键字 | 作用 | 使用位置 |
|---|---|---|
override | 确认函数确实覆盖了基类虚函数 | 成员函数声明末尾 |
final | 禁止派生类进一步覆盖 | 虚函数或类本身 |
#include <iostream>
class Base {
public:
virtual void foo() const { std::cout << "Base::foo\n"; }
virtual void bar() { std::cout << "Base::bar\n"; }
virtual void baz() { std::cout << "Base::baz\n"; }
};
class Derived : public Base {
public:
// ✅ 正确覆盖
void foo() const override { std::cout << "Derived::foo\n"; }
// ⚠️ 如果拼写错误或签名不匹配,编译器会报错
// void foo() override { } // Error: const 不匹配
// final:禁止进一步覆盖
void bar() override final { std::cout << "Derived::bar (final)\n"; }
};
class MoreDerived : public Derived {
public:
void foo() const override { std::cout << "MoreDerived::foo\n"; }
// void bar() override { } // Error: bar 已被 final 禁止
};
// final 类:不可被继承
class Sealed final : public MoreDerived {};
// class Illegal : public Sealed {}; // Error: Sealed 是 final 的
int main() {
MoreDerived md;
md.foo(); // MoreDerived::foo
md.bar(); // Derived::bar (final)
return 0;
}
5. 多态原理(虚表 vtable)
每个含虚函数的类都有一个虚函数表(vtable),每个含虚函数的对象都有一个虚表指针(vptr)。
┌──────────────┐
│ Shape 对象 │
│ vptr ──────────→ Shape vtable
│ 其他数据 │ [0] draw()
└──────────────┘ [1] ~Shape()
┌──────────────┐
│ Circle 对象 │
│ vptr ──────────→ Circle vtable
│ radius_ │ [0] Circle::draw()
│ 其他数据 │ [1] ~Circle()
└──────────────┘
#include <iostream>
#include <cstddef>
struct A {
virtual void f() { std::cout << "A::f\n"; }
virtual void g() { std::cout << "A::g\n"; }
int x = 1;
};
struct B : A {
void f() override { std::cout << "B::f\n"; }
int y = 2;
};
int main() {
A a;
B b;
std::cout << "sizeof(A) = " << sizeof(A) << "\n"; // 16 (vptr + int + padding)
std::cout << "sizeof(B) = " << sizeof(B) << "\n"; // 24 (vptr + int + int + padding)
// 通过基类指针调用 → vtable 查找 → 运行时分派
A* p = &b;
p->f(); // B::f(通过 vtable 分派到 B 的实现)
p->g(); // A::g(B 没有覆盖 g,回退到 A 的实现)
return 0;
}
💡 提示:虚函数调用有微小开销(一次指针间接寻址)。在性能极度敏感的热循环中可考虑
final类或非虚函数。
6. 菱形继承与虚继承
菱形继承:D 同时继承 B 和 C,而 B 和 C 都继承自 A。
A
/ \
B C
\ /
D
#include <iostream>
// ❌ 菱形继承问题:D 中有两份 A 的数据
struct A_bad { int x = 1; };
struct B_bad : A_bad {};
struct C_bad : A_bad {};
struct D_bad : B_bad, C_bad {};
// ✅ 虚继承:共享基类实例
struct A {
int x = 1;
virtual void print() { std::cout << "A::x=" << x << "\n"; }
};
struct B : virtual A { // 虚继承
void print() override { std::cout << "B::x=" << x << "\n"; }
};
struct C : virtual A { // 虚继承
void print() override { std::cout << "C::x=" << x << "\n"; }
};
struct D : B, C {
void print() override { std::cout << "D::x=" << x << "\n"; }
};
int main() {
// 菱形继承问题
D_bad d_bad;
// d_bad.x = 5; // 歧义:B_bad::A_bad::x 还是 C_bad::A_bad::x?
d_bad.B_bad::A_bad::x = 10;
d_bad.C_bad::A_bad::x = 20;
std::cout << "B_bad::A_bad::x = " << d_bad.B_bad::A_bad::x << "\n"; // 10
std::cout << "C_bad::A_bad::x = " << d_bad.C_bad::A_bad::x << "\n"; // 20
// 虚继承:只有一份 A
D d;
d.x = 42; // 无歧义
d.print(); // D::x=42
B* bp = &d;
bp->print(); // D::x=42(多态)
return 0;
}
⚠️ 注意:虚继承有额外的内存和性能开销(虚基类表)。设计时应尽量避免菱形继承。
7. dynamic_cast 与 RTTI
#include <iostream>
#include <typeinfo>
class Base {
public:
virtual ~Base() = default; // 需要虚函数才能使用 dynamic_cast
};
class DerivedA : public Base {
public:
void showA() { std::cout << "DerivedA\n"; }
};
class DerivedB : public Base {
public:
void showB() { std::cout << "DerivedB\n"; }
};
void process(Base* ptr) {
// dynamic_cast:安全的向下转换
if (auto* a = dynamic_cast<DerivedA*>(ptr)) {
a->showA();
} else if (auto* b = dynamic_cast<DerivedB*>(ptr)) {
b->showB();
} else {
std::cout << "未知类型\n";
}
// typeid:获取运行时类型信息
std::cout << "类型: " << typeid(*ptr).name() << "\n";
}
int main() {
DerivedA a;
DerivedB b;
process(&a); // DerivedA
process(&b); // DerivedB
// dynamic_cast 引用转换失败会抛出 std::bad_cast
try {
Base& ref = a;
auto& bRef = dynamic_cast<DerivedB&>(ref); // 失败
} catch (const std::bad_cast& e) {
std::cout << "转换失败: " << e.what() << "\n";
}
return 0;
}
| 转换方式 | 安全性 | 适用场景 |
|---|---|---|
static_cast | 编译期检查,不检查运行时类型 | 已知类型转换 |
dynamic_cast | 运行时检查,失败返回 nullptr 或抛异常 | 多态类型安全向下转换 |
const_cast | 移除/添加 const | 与旧 API 交互 |
reinterpret_cast | 不安全,底层重新解释位模式 | 系统编程 |
8. 多态设计模式:策略模式
#include <iostream>
#include <memory>
#include <string>
#include <vector>
// 策略接口
class SortStrategy {
public:
virtual ~SortStrategy() = default;
virtual void sort(std::vector<int>& data) = 0;
virtual std::string name() const = 0;
};
// 具体策略:冒泡排序
class BubbleSort : public SortStrategy {
public:
void sort(std::vector<int>& data) override {
std::cout << "使用冒泡排序...\n";
for (size_t i = 0; i < data.size(); ++i) {
for (size_t j = 0; j < data.size() - 1 - i; ++j) {
if (data[j] > data[j + 1]) {
std::swap(data[j], data[j + 1]);
}
}
}
}
std::string name() const override { return "BubbleSort"; }
};
// 具体策略:选择排序
class SelectionSort : public SortStrategy {
public:
void sort(std::vector<int>& data) override {
std::cout << "使用选择排序...\n";
for (size_t i = 0; i < data.size(); ++i) {
size_t minIdx = i;
for (size_t j = i + 1; j < data.size(); ++j) {
if (data[j] < data[minIdx]) minIdx = j;
}
std::swap(data[i], data[minIdx]);
}
}
std::string name() const override { return "SelectionSort"; }
};
// 上下文:持有策略对象
class Sorter {
std::unique_ptr<SortStrategy> strategy_;
public:
void setStrategy(std::unique_ptr<SortStrategy> s) {
strategy_ = std::move(s);
}
void doSort(std::vector<int>& data) {
if (strategy_) {
std::cout << "策略: " << strategy_->name() << "\n";
strategy_->sort(data);
}
}
};
int main() {
std::vector<int> data = {64, 25, 12, 22, 11};
Sorter sorter;
sorter.setStrategy(std::make_unique<BubbleSort>());
sorter.doSort(data);
data = {64, 25, 12, 22, 11}; // 重置数据
sorter.setStrategy(std::make_unique<SelectionSort>());
sorter.doSort(data);
for (int v : data) std::cout << v << " ";
std::cout << "\n";
return 0;
}
实际场景:GUI 组件系统
#include <iostream>
#include <string>
#include <vector>
#include <memory>
class Widget {
protected:
std::string id_;
int x_, y_;
public:
Widget(const std::string& id, int x, int y)
: id_(id), x_(x), y_(y) {}
virtual ~Widget() = default;
virtual void render() const = 0;
virtual void onClick() { std::cout << id_ << " 被点击\n"; }
bool contains(int px, int py) const {
return px >= x_ && px <= x_ + width() && py >= y_ && py <= y_ + height();
}
virtual int width() const = 0;
virtual int height() const = 0;
};
class Button : public Widget {
std::string text_;
public:
Button(const std::string& id, int x, int y, const std::string& text)
: Widget(id, x, y), text_(text) {}
void render() const override {
std::cout << "[Button " << id_ << "] \"" << text_
<< "\" at (" << x_ << "," << y_ << ")\n";
}
int width() const override { return static_cast<int>(text_.size()) * 8 + 20; }
int height() const override { return 30; }
};
class Label : public Widget {
std::string text_;
public:
Label(const std::string& id, int x, int y, const std::string& text)
: Widget(id, x, y), text_(text) {}
void render() const override {
std::cout << "[Label " << id_ << "] \"" << text_
<< "\" at (" << x_ << "," << y_ << ")\n";
}
void onClick() override {
std::cout << "Label 不响应点击\n";
}
int width() const override { return static_cast<int>(text_.size()) * 8; }
int height() const override { return 20; }
};
int main() {
std::vector<std::unique_ptr<Widget>> widgets;
widgets.push_back(std::make_unique<Button>("btn_ok", 10, 10, "确定"));
widgets.push_back(std::make_unique<Button>("btn_cancel", 100, 10, "取消"));
widgets.push_back(std::make_unique<Label>("lbl_title", 10, 50, "用户注册"));
for (const auto& w : widgets) {
w->render();
w->onClick();
}
return 0;
}
扩展阅读
- cppreference — Virtual functions
- cppreference — dynamic_cast
- C++ Core Guidelines — C.120-Virtual
- Effective C++ 条款 34-40(继承与面向对象设计)
- Compiler Explorer — vtable 生成(可视化查看 vtable 实现)