第14章 类继承

继承的概念与原理

继承是面向对象编程的核心特性之一,它允许从已有类(基类/父类)派生出新类(派生类/子类),实现代码重用和类型层次结构的建立。在专家级C++开发中,继承不仅是代码组织工具,更是性能优化和系统设计的关键技术。

继承的核心原理

  1. 代码重用(Code Reuse):派生类自动继承基类的成员变量和成员函数,避免重复代码
  2. 层次结构(Hierarchy):建立类的层次关系,反映现实世界的分类体系
  3. 类型关系(Type Relationship):派生类与基类之间形成”is-a”关系
  4. 多态基础(Polymorphism Foundation):为运行时多态提供基础

继承的底层实现

虚函数表(vtable)机制

虚函数表是C++实现运行时多态的核心机制,理解其底层实现对于性能优化至关重要。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 虚函数表布局分析
class Base {
public:
virtual void func1() {}
virtual void func2() {}
int value;
};

class Derived : public Base {
public:
void func1() override {}
virtual void func3() {}
int derivedValue;
};

// 内存布局(32位系统):
// Base对象: [vptr] [value]
// Derived对象: [vptr] [Base::value] [derivedValue]
//
// 虚函数表:
// Base vtable: [Base::func1, Base::func2]
// Derived vtable: [Derived::func1, Base::func2, Derived::func3]

虚函数调用的性能开销

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 虚函数调用 vs 非虚函数调用性能对比
void benchmarkVirtualCalls() {
Base* basePtr = new Derived();

// 虚函数调用:需要通过vptr查找vtable,再调用对应函数
// 开销:内存访问(vptr) + 内存访问(vtable) + 函数调用
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 10000000; ++i) {
basePtr->func1(); // 虚函数调用
}
auto end = std::chrono::high_resolution_clock::now();
std::cout << "Virtual calls: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()
<< "ms" << std::endl;

delete basePtr;
}

继承与内存布局

单一继承的内存布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Base {
private:
int a;
public:
virtual void foo() {}
};

class Derived : public Base {
private:
int b;
public:
void foo() override {}
virtual void bar() {}
};

// 内存布局(64位系统):
// Base: [vptr (8 bytes)] [a (4 bytes)] [padding (4 bytes)] = 16 bytes
// Derived: [vptr (8 bytes)] [Base::a (4 bytes)] [padding (4 bytes)] [b (4 bytes)] [padding (4 bytes)] = 24 bytes

内存对齐优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 优化前:内存浪费
class BadLayout {
public:
char c; // 1 byte + 7 bytes padding
virtual void foo(); // 8 bytes vptr
int i; // 4 bytes + 4 bytes padding
}; // 总大小:24 bytes

// 优化后:紧凑布局
class GoodLayout {
public:
virtual void foo(); // 8 bytes vptr
int i; // 4 bytes
char c; // 1 byte + 3 bytes padding
}; // 总大小:16 bytes

继承的语法与实现

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// 基类定义
class Base {
protected:
int value;
public:
Base(int v) : value(v) {
std::cout << "Base constructor called" << std::endl;
}

virtual ~Base() {
std::cout << "Base destructor called" << std::endl;
}

void showValue() const {
std::cout << "Base value: " << value << std::endl;
}

virtual void doSomething() {
std::cout << "Base::doSomething()" << std::endl;
}
};

// 派生类定义
class Derived : public Base {
private:
std::string name;
public:
// 派生类构造函数
Derived(int v, const std::string& n) : Base(v), name(n) {
std::cout << "Derived constructor called" << std::endl;
}

~Derived() {
std::cout << "Derived destructor called" << std::endl;
}

// 新增成员函数
void showName() const {
std::cout << "Derived name: " << name << std::endl;
}

// 重写基类虚函数
void doSomething() override {
std::cout << "Derived::doSomething()" << std::endl;
Base::doSomething(); // 调用基类方法
}
};

// 使用示例
void useInheritance() {
Derived d(42, "Derived");
d.showValue(); // 继承自基类
d.showName(); // 派生类新增
d.doSomething(); // 多态调用
}

继承的类型

C++支持三种继承方式,不同的继承方式会影响基类成员在派生类中的可访问性,同时也会影响编译器的优化策略和对象的内存布局。

公有继承(public inheritance)

核心特性:基类的public成员在派生类中仍然是publicprotected成员仍然是protectedprivate成员不可访问。

应用场景:体现”is-a”关系,是最常用的继承方式。

底层实现:公有继承的对象内存布局中,基类子对象位于派生类对象的起始位置,确保指针转换的正确性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Shape {
public:
virtual double area() const = 0;
virtual void draw() const = 0;
virtual ~Shape() = default;
};

class Circle : public Shape {
private:
double radius;
public:
explicit Circle(double r) : radius(r) {}

double area() const override {
return M_PI * radius * radius;
}

void draw() const override {
std::cout << "Drawing a circle with radius " << radius << std::endl;
}
};

// 内存布局(64位系统):
// Circle对象: [vptr (8 bytes)] [radius (8 bytes)] = 16 bytes

保护继承(protected inheritance)

核心特性:基类的publicprotected成员在派生类中都变为protectedprivate成员不可访问。

应用场景:体现”is-implemented-in-terms-of”关系,用于实现细节的重用。

底层实现:保护继承的内存布局与公有继承相同,但编译器会限制对基类成员的访问权限。

私有继承(private inheritance)

核心特性:基类的publicprotected成员在派生类中都变为privateprivate成员不可访问。

应用场景:体现”is-implemented-in-terms-of”关系,用于完全隐藏基类接口。

底层实现:私有继承的内存布局与公有继承相同,但编译器会进一步限制对基类成员的访问权限。

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
// 私有继承示例:实现组合的一种方式
class String {
private:
std::vector<char> buffer; // 组合方式
public:
// 接口实现
void push_back(char c) {
buffer.push_back(c);
}

size_t size() const {
return buffer.size();
}
};

// 等价的私有继承方式
class String : private std::vector<char> {
public:
// 接口实现,显式暴露需要的方法
using std::vector<char>::push_back;
using std::vector<char>::size;

// 可以添加额外的方法
bool startsWith(char c) const {
return !empty() && front() == c;
}
};

// 私有继承 vs 组合的性能对比
// 私有继承:空基类优化(EBO)可能适用,减少内存开销
// 组合:更清晰的接口,避免继承带来的复杂性

继承方式的性能影响

继承方式内存开销访问速度代码复杂度适用场景
公有继承基类大小 + 派生类大小直接访问is-a关系
保护继承基类大小 + 派生类大小直接访问实现细节重用
私有继承基类大小 + 派生类大小(可能有EBO优化)直接访问完全隐藏实现
组合成员对象大小 + 派生类大小间接访问(通过成员)has-a关系

构造函数与析构函数的继承

构造函数的调用顺序

核心规则:派生类对象创建时,构造函数的调用顺序是从基类到派生类。

  1. 基类构造函数:先调用最顶层基类的构造函数
  2. 成员变量构造函数:按照声明顺序调用成员变量的构造函数
  3. 派生类构造函数:最后调用派生类自身的构造函数

性能优化:构造函数初始化列表可以避免成员变量的默认构造和赋值操作,直接进行初始化。

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
34
class Base1 {
public:
Base1() {
std::cout << "Base1 constructor" << std::endl;
}
};

class Base2 {
public:
Base2() {
std::cout << "Base2 constructor" << std::endl;
}
};

class Derived : public Base1, public Base2 {
private:
std::string name; // 成员变量
std::vector<int> data;
public:
// 优化:使用初始化列表,避免默认构造
Derived() : Base1(), Base2(), name("Derived"), data(10, 0) {
std::cout << "Derived constructor" << std::endl;
}

// 优化:使用委托构造函数
Derived(int size) : Derived() {
data.resize(size);
}
};

// 调用顺序:Base1 → Base2 → std::string → std::vector → Derived
void testConstructorOrder() {
Derived d;
}

析构函数的调用顺序

核心规则:派生类对象销毁时,析构函数的调用顺序与构造函数相反。

  1. 派生类析构函数:先调用派生类自身的析构函数
  2. 成员变量析构函数:按照声明顺序的逆序调用成员变量的析构函数
  3. 基类析构函数:最后调用基类的析构函数

异常处理:析构函数不应该抛出异常,否则可能导致程序终止。

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
class Base {
public:
virtual ~Base() noexcept { // 虚析构函数,标记为noexcept
std::cout << "Base destructor" << std::endl;
}
};

class Derived : public Base {
private:
std::string name;
std::unique_ptr<int[]> data;
public:
Derived() : data(new int[100]) {}

~Derived() noexcept override { // 标记为noexcept
std::cout << "Derived destructor" << std::endl;
// 析构函数中不应抛出异常
}
};

// 调用顺序:Derived → std::unique_ptr → std::string → Base
void testDestructorOrder() {
Base* ptr = new Derived();
delete ptr; // 多态调用析构函数
}

虚析构函数的重要性

核心原则:当使用基类指针指向派生类对象时,基类的析构函数必须声明为virtual,以确保派生类的析构函数被正确调用。

未使用虚析构函数的风险

  • 只调用基类析构函数,不调用派生类析构函数
  • 可能导致资源泄漏(如文件句柄、内存等)
  • 程序行为未定义

现代C++实践

  • 对于有虚函数的类,析构函数应该声明为virtual
  • 对于没有虚函数的类,析构函数不应该声明为virtual(避免vptr开销)
  • 使用override关键字明确标记析构函数的重写

构造函数的继承(C++11+)

核心特性:C++11引入了继承构造函数,允许派生类继承基类的构造函数。

高级用法

  • 继承构造函数与默认成员初始化器的结合
  • 继承构造函数与委托构造函数的结合
  • 继承构造函数的显式控制
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
34
35
36
37
38
39
40
41
class Base {
private:
int value;
public:
Base() : value(0) {}
Base(int v) : value(v) {}
Base(const std::string& s) : value(std::stoi(s)) {}

// 移动构造函数
Base(Base&& other) noexcept : value(std::exchange(other.value, 0)) {}

// 拷贝构造函数
Base(const Base& other) : value(other.value) {}
};

class Derived : public Base {
private:
std::string name = "Default"; // 默认成员初始化器
public:
// 继承基类的构造函数
using Base::Base;

// 额外的构造函数
Derived(int v, const std::string& n) : Base(v), name(n) {}

// 委托构造函数
Derived(const char* cstr) : Derived(std::string(cstr)) {}

// 显式控制继承的构造函数
Derived& operator=(Derived&&) noexcept = default;
Derived& operator=(const Derived&) = default;
};

// 使用示例
void useInheritedConstructors() {
Derived d1(42); // 使用继承的 Base(int)
Derived d2("123"); // 使用继承的 Base(const std::string&)
Derived d3(100, "Custom"); // 使用派生类自定义构造函数
Derived d4(d3); // 使用继承的拷贝构造函数
Derived d5(std::move(d4)); // 使用继承的移动构造函数
}

继承构造函数的限制

  1. 默认构造函数:基类的默认构造函数不会被继承,需要显式定义
  2. 拷贝/移动构造函数:基类的拷贝和移动构造函数不会被继承
  3. 构造函数冲突:如果派生类有与基类构造函数签名相同的构造函数,继承的构造函数会被隐藏
  4. 访问控制:继承的构造函数保持基类的访问级别
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Base {
protected:
Base(int v) {}
public:
Base() {}
};

class Derived : public Base {
public:
using Base::Base; // 继承所有构造函数

// Base(int) 继承后变为 protected
};

void testInheritedConstructorAccess() {
Derived d1; // 正确:使用继承的 public Base()
// Derived d2(42); // 错误:继承的 Base(int) 是 protected
}

函数覆盖与隐藏

函数覆盖(Function Overriding)

核心特性:派生类重新实现基类的虚函数,实现运行时多态。

实现条件

  • 基类函数必须声明为virtual
  • 派生类函数必须使用override关键字(C++11+)
  • 函数签名(返回类型、函数名、参数列表)必须完全相同
  • 返回类型可以是协变类型(covariant return type)

底层实现

  • 函数覆盖会修改派生类的虚函数表(vtable),将对应的函数指针替换为派生类的实现
  • 虚函数调用通过vptr和vtable实现运行时绑定
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
34
35
36
class Base {
public:
virtual void doSomething() {
std::cout << "Base::doSomething()" << std::endl;
}

virtual Base* clone() const {
return new Base(*this);
}

virtual ~Base() = default;
};

class Derived : public Base {
public:
void doSomething() override {
std::cout << "Derived::doSomething()" << std::endl;
Base::doSomething(); // 显式调用基类方法
}

// 协变返回类型:返回Derived*而不是Base*
Derived* clone() const override {
return new Derived(*this);
}

// 使用final关键字防止进一步覆盖
virtual void finalMethod() final {
std::cout << "Derived::finalMethod()" << std::endl;
}
};

// 错误:无法覆盖final方法
// class FinalDerived : public Derived {
// public:
// void finalMethod() 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
34
35
36
37
38
39
40
#include <memory>

class Base {
public:
virtual std::unique_ptr<Base> create() const {
return std::make_unique<Base>();
}

virtual Base* clone() const {
return new Base(*this);
}

virtual ~Base() = default;
};

class Derived : public Base {
public:
// 协变返回类型:返回unique_ptr<Derived>
std::unique_ptr<Derived> create() const override {
return std::make_unique<Derived>();
}

// 协变返回类型:返回Derived*
Derived* clone() const override {
return new Derived(*this);
}
};

class MoreDerived : public Derived {
public:
// 协变返回类型:返回unique_ptr<MoreDerived>
std::unique_ptr<MoreDerived> create() const override {
return std::make_unique<MoreDerived>();
}

// 协变返回类型:返回MoreDerived*
MoreDerived* clone() const override {
return new MoreDerived(*this);
}
};

函数隐藏(Function Hiding)

核心特性:派生类定义了与基类同名的函数,隐藏了基类的函数(无论是否为虚函数)。

详细分析

  • 函数隐藏是作用域问题,不是多态问题
  • 派生类的函数会隐藏基类中所有同名函数,无论参数列表如何
  • 可以使用using声明来显式引入基类函数到派生类作用域
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
34
35
36
37
38
39
40
41
class Base {
public:
void show(int x) {
std::cout << "Base::show(int): " << x << std::endl;
}

void show(double x) {
std::cout << "Base::show(double): " << x << std::endl;
}

virtual void display() {
std::cout << "Base::display()" << std::endl;
}
};

class Derived : public Base {
public:
// 隐藏基类的所有show函数
void show(const std::string& s) {
std::cout << "Derived::show(string): " << s << std::endl;
}

// 隐藏基类的display函数(即使它是虚函数)
void display() {
std::cout << "Derived::display()" << std::endl;
}

// 显式引入基类的show函数
using Base::show;
};

// 使用示例
void testFunctionHiding() {
Derived d;
d.show(42); // 调用基类的show(int)
d.show(3.14); // 调用基类的show(double)
d.show("Hello"); // 调用派生类的show(string)

Base* basePtr = &d;
basePtr->display(); // 多态调用Derived::display()
}

虚函数的性能优化

核心策略

  • 避免在性能关键路径上使用虚函数
  • 考虑使用静态多态(CRTP)替代动态多态
  • 合理使用final关键字帮助编译器优化
  • 利用内联函数减少函数调用开销
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
34
35
36
37
38
39
40
41
42
43
// 静态多态示例(CRTP)
template <typename Derived>
class BaseCRTP {
public:
void doSomething() {
// 静态分派,无虚函数开销
static_cast<Derived*>(this)->implementation();
}
};

class DerivedCRTP : public BaseCRTP<DerivedCRTP> {
public:
void implementation() {
std::cout << "DerivedCRTP::implementation()" << std::endl;
}
};

// 性能对比
void benchmarkPolymorphism() {
// 动态多态
Base* basePtr = new Derived();
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 10000000; ++i) {
basePtr->doSomething();
}
auto end = std::chrono::high_resolution_clock::now();
std::cout << "Dynamic polymorphism: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()
<< "ms" << std::endl;

// 静态多态
DerivedCRTP derivedCRTP;
start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 10000000; ++i) {
derivedCRTP.doSomething();
}
end = std::chrono::high_resolution_clock::now();
std::cout << "Static polymorphism: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()
<< "ms" << std::endl;

delete basePtr;
}

多重继承与虚拟继承

多重继承的概念

核心特性:C++允许一个派生类从多个基类继承,实现更复杂的类型层次结构。

应用场景

  • 实现多个接口(接口继承)
  • 组合多个功能模块(实现继承)
  • 混合不同的行为特性(mixin模式)
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
34
35
36
37
38
39
40
41
class Interface1 {
public:
virtual void method1() = 0;
virtual ~Interface1() = default;
};

class Interface2 {
public:
virtual void method2() = 0;
virtual ~Interface2() = default;
};

// 多重继承实现多个接口
class ConcreteClass : public Interface1, public Interface2 {
public:
void method1() override {
std::cout << "ConcreteClass::method1()" << std::endl;
}

void method2() override {
std::cout << "ConcreteClass::method2()" << std::endl;
}
};

// Mixin模式示例
template <typename T>
class LoggerMixin {
public:
void log(const std::string& message) {
std::cout << "[LOG] " << message << std::endl;
}
};

class DataProcessor : public LoggerMixin<DataProcessor> {
public:
void process() {
log("Starting processing");
// 处理逻辑
log("Processing completed");
}
};

多重继承的内存布局

核心分析:多重继承的对象包含多个基类子对象,每个基类子对象都有自己的内存布局。

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
class Base1 {
public:
virtual void foo() {}
int a;
};

class Base2 {
public:
virtual void bar() {}
int b;
};

class Derived : public Base1, public Base2 {
public:
void foo() override {}
void bar() override {}
int c;
};

// 内存布局(64位系统):
// Derived对象:
// [Base1 vptr (8 bytes)] [Base1::a (4 bytes)] [padding (4 bytes)]
// [Base2 vptr (8 bytes)] [Base2::b (4 bytes)] [padding (4 bytes)]
// [Derived::c (4 bytes)] [padding (4 bytes)]
// 总大小:40 bytes

菱形继承问题

核心问题:当一个类从两个不同的路径继承同一个基类时,会产生重复的基类子对象。

深度分析

  • 内存浪费:每个路径都有一个基类子对象
  • 数据不一致:不同路径的基类子对象可能有不同的值
  • 访问歧义:直接访问基类成员会产生编译错误
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
34
35
36
37
38
// 菱形继承结构
class Base {
public:
int value;
Base() : value(42) {
std::cout << "Base constructor, this: " << this << std::endl;
}
};

class Derived1 : public Base {
public:
int d1;
};

class Derived2 : public Base {
public:
int d2;
};

class FinalDerived : public Derived1, public Derived2 {
public:
int fd;
};

// 问题:FinalDerived包含两个Base子对象
void testDiamondProblem() {
FinalDerived obj;
std::cout << "FinalDerived size: " << sizeof(obj) << std::endl;
std::cout << "obj.Derived1::value: " << obj.Derived1::value << std::endl;
std::cout << "obj.Derived2::value: " << obj.Derived2::value << std::endl;

obj.Derived1::value = 100;
obj.Derived2::value = 200;

std::cout << "After modification:" << std::endl;
std::cout << "obj.Derived1::value: " << obj.Derived1::value << std::endl;
std::cout << "obj.Derived2::value: " << obj.Derived2::value << std::endl;
}

虚拟继承(Virtual Inheritance)

核心解决方案:使用虚拟继承解决菱形继承问题,确保最终派生类只包含一个共享的基类子对象。

底层实现

  • 虚拟继承会为每个中间派生类添加一个虚基类指针(vbptr)
  • 虚基类指针指向虚基类表(vbtable),表中存储了到虚基类子对象的偏移量
  • 最终派生类只包含一个虚基类子对象
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 Base {
public:
int value;
Base() : value(42) {
std::cout << "Base constructor, this: " << this << std::endl;
}
};

// 使用虚拟继承
class Derived1 : public virtual Base {
public:
int d1;
};

class Derived2 : public virtual Base {
public:
int d2;
};

class FinalDerived : public Derived1, public Derived2 {
public:
int fd;
};

// 解决方案:FinalDerived只包含一个Base子对象
void testVirtualInheritance() {
FinalDerived obj;
std::cout << "FinalDerived size: " << sizeof(obj) << std::endl;
obj.value = 100; // 无歧义,直接访问
std::cout << "Base value: " << obj.value << std::endl;
std::cout << "obj.Derived1::value: " << obj.Derived1::value << std::endl;
std::cout << "obj.Derived2::value: " << obj.Derived2::value << std::endl;
}

虚拟继承的内存布局

1
2
3
4
5
6
7
8
9
10
11
// 虚拟继承的内存布局(64位系统):
// FinalDerived对象:
// [Derived1 vptr (8 bytes)] [Derived1::d1 (4 bytes)] [padding (4 bytes)]
// [Derived2 vptr (8 bytes)] [Derived2::d2 (4 bytes)] [padding (4 bytes)]
// [FinalDerived::fd (4 bytes)] [padding (4 bytes)]
// [Base::value (4 bytes)] [padding (4 bytes)]
// 总大小:48 bytes

// 虚基类表(vbtable):
// Derived1 vbtable: [0, offset to Base]
// Derived2 vbtable: [0, offset to Base]

虚拟继承的构造函数调用

核心规则:在虚拟继承中,虚拟基类的构造函数由最终派生类直接调用,而不是由中间派生类调用。

调用顺序

  1. 所有虚拟基类的构造函数(按声明顺序)
  2. 非虚拟基类的构造函数(按声明顺序)
  3. 成员变量的构造函数(按声明顺序)
  4. 派生类自身的构造函数
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
34
35
36
37
38
39
40
41
42
class VirtualBase {
public:
VirtualBase(int v) : value(v) {
std::cout << "VirtualBase constructor: " << value << std::endl;
}
int value;
};

class NonVirtualBase {
public:
NonVirtualBase() {
std::cout << "NonVirtualBase constructor" << std::endl;
}
};

class Derived1 : public virtual VirtualBase, public NonVirtualBase {
public:
Derived1() : VirtualBase(100) { // 非最终派生类时调用
std::cout << "Derived1 constructor" << std::endl;
}
};

class Derived2 : public virtual VirtualBase {
public:
Derived2() : VirtualBase(200) { // 非最终派生类时调用
std::cout << "Derived2 constructor" << std::endl;
}
};

class FinalDerived : public Derived1, public Derived2 {
public:
// 最终派生类直接调用虚拟基类构造函数
FinalDerived() : VirtualBase(300), Derived1(), Derived2() {
std::cout << "FinalDerived constructor" << std::endl;
}
};

// 调用顺序:VirtualBase(300) → NonVirtualBase → Derived1 → Derived2 → FinalDerived
void testVirtualBaseConstructor() {
FinalDerived obj;
std::cout << "VirtualBase value: " << obj.value << std::endl;
}

多重继承的性能影响

核心分析

  • 内存开销:多重继承增加对象大小,虚拟继承额外增加vbptr开销
  • 访问开销:虚拟继承需要通过vbptr和vbtable访问虚基类成员
  • 构造/析构开销:多重继承增加构造和析构的复杂度

性能对比

继承方式内存开销访问速度构造/析构速度复杂度
单一继承
多重继承
虚拟继承

多重继承的最佳实践

核心原则

  • 优先使用单一继承
  • 对于接口继承,使用多重继承是合理的
  • 对于实现继承,谨慎使用多重继承
  • 避免深层的多重继承层次
  • 合理使用虚拟继承解决菱形继承问题

现代C++替代方案

  • 使用组合替代实现继承
  • 使用模板和CRTP实现静态多态
  • 使用接口和依赖注入
  • 使用std::variant和std::any实现类型擦除
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
// 组合替代多重继承
class Logger {
public:
void log(const std::string& message) {
std::cout << "[LOG] " << message << std::endl;
}
};

class Database {
public:
void save(const std::string& data) {
std::cout << "Saving data: " << data << std::endl;
}
};

class UserService {
private:
Logger logger;
Database database;
public:
void createUser(const std::string& name) {
logger.log("Creating user: " + name);
database.save(name);
logger.log("User created: " + name);
}
};

继承的最佳实践

1. 优先使用组合而非继承

核心原则:当派生类与基类之间不是”is-a”关系时,应该优先使用组合而非继承。

组合的优势

  • 降低类之间的耦合度
  • 避免继承带来的复杂性(如菱形继承)
  • 更灵活的代码组织方式
  • 符合单一职责原则
  • 支持运行时行为变化(通过依赖注入)

现代C++实践

  • 使用智能指针管理组合对象的生命周期
  • 使用依赖注入提高代码的可测试性
  • 使用策略模式和桥接模式替代深度继承
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
34
35
36
37
38
39
// 继承方式(不推荐,因为Car不是一个Engine)
class Engine {
public:
virtual void start() { /* 启动引擎 */ }
virtual void stop() { /* 停止引擎 */ }
virtual ~Engine() = default;
};

class Car : public Engine { // 错误:Car不是一个Engine
public:
void drive() { /* 驾驶汽车 */ }
};

// 组合方式(推荐)
class Car {
private:
std::unique_ptr<Engine> engine; // Car有一个Engine
public:
explicit Car(std::unique_ptr<Engine> e) : engine(std::move(e)) {}

void start() {
engine->start();
}

void stop() {
engine->stop();
}

void drive() { /* 驾驶汽车 */ }
};

// 使用示例
void useCar() {
auto engine = std::make_unique<GasolineEngine>();
Car car(std::move(engine));
car.start();
car.drive();
car.stop();
}

2. 合理设计继承层次

核心原则:继承层次应该保持合理的深度,避免过深的继承链。

最佳实践

  • 继承层次通常不超过3-4层
  • 每一层都应该有明确的职责和抽象
  • 使用接口(纯虚类)定义行为契约
  • 避免在继承层次中混用不同的继承方式
  • 考虑使用组合和委托模式简化复杂的继承层次

SOLID原则应用

  • 单一职责原则:每个类应该只有一个变化的理由
  • 开放-封闭原则:类应该对扩展开放,对修改封闭
  • 里氏替换原则:派生类应该能够替换基类而不破坏程序的正确性
  • 接口隔离原则:客户端不应该依赖它不使用的接口
  • 依赖倒置原则:依赖于抽象,而不是具体实现
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
// 良好的继承层次
class Shape { // 抽象基类
public:
virtual double area() const = 0;
virtual void draw() const = 0;
virtual ~Shape() = default;
};

class TwoDimensionalShape : public Shape { // 中间抽象类
public:
virtual double perimeter() const = 0;
};

class Circle : public TwoDimensionalShape { // 具体类
private:
double radius;
public:
explicit Circle(double r) : radius(r) {}

double area() const override {
return M_PI * radius * radius;
}

double perimeter() const override {
return 2 * M_PI * radius;
}

void draw() const override {
// 绘制圆形
}
};

3. 正确使用访问控制

核心原则:根据成员的用途选择合适的访问修饰符。

最佳实践

  • public:只用于类的接口方法
  • protected:用于需要被派生类访问的内部实现
  • private:用于完全内部的实现细节
  • 避免使用friend关键字,除非确实必要
  • 考虑使用Pimpl模式进一步隐藏实现细节

现代C++特性

  • 使用[[nodiscard]]标记重要的返回值
  • 使用constexprconsteval提高性能
  • 使用noexcept标记不会抛出异常的函数
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class Widget {
private:
// Pimpl模式
class Impl;
std::unique_ptr<Impl> impl;
public:
Widget();
~Widget();
Widget(Widget&&) noexcept;
Widget& operator=(Widget&&) noexcept;

// 公共接口
void doSomething();

// 标记为nodiscard的方法
[[nodiscard]] bool isValid() const;
};

// 实现文件
class Widget::Impl {
private:
int data;
std::string name;
public:
Impl() : data(0) {}

void doSomething() {
// 实现细节
}

bool isValid() const {
return data >= 0;
}
};

Widget::Widget() : impl(std::make_unique<Impl>()) {}
Widget::~Widget() = default;
Widget::Widget(Widget&&) noexcept = default;
Widget& Widget::operator=(Widget&&) noexcept = default;

void Widget::doSomething() {
impl->doSomething();
}

bool Widget::isValid() const {
return impl->isValid();
}

4. 实现多态时的注意事项

核心原则:正确实现虚函数,确保多态行为的正确性。

最佳实践

  • 基类析构函数应该声明为virtual
  • 派生类重写虚函数时使用override关键字
  • 避免在构造函数和析构函数中调用虚函数
  • 考虑使用final关键字防止不必要的覆盖
  • 合理使用纯虚函数定义接口

性能优化

  • 避免在性能关键路径上使用虚函数
  • 考虑使用静态多态(CRTP)替代动态多态
  • 利用编译器优化(如内联、常量传播)
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
34
class Base {
public:
Base() {
// 错误:构造函数中调用虚函数不会产生多态行为
// doSomething(); // 只会调用Base::doSomething()
}

virtual ~Base() {
// 错误:析构函数中调用虚函数不会产生多态行为
// doSomething(); // 只会调用Base::doSomething()
}

virtual void doSomething() {
std::cout << "Base::doSomething()" << std::endl;
}

virtual void doSomethingElse() = 0; // 纯虚函数
};

class Derived : public Base {
public:
void doSomething() override {
std::cout << "Derived::doSomething()" << std::endl;
}

void doSomethingElse() override {
std::cout << "Derived::doSomethingElse()" << std::endl;
}

// 标记为final的方法
virtual void finalMethod() final {
std::cout << "Derived::finalMethod()" << std::endl;
}
};

5. 处理菱形继承

核心原则:当需要多重继承时,正确使用虚拟继承解决菱形继承问题。

最佳实践

  • 只在必要时使用多重继承
  • 优先使用接口(纯虚类)作为多重继承的基类
  • 对于有状态的基类,使用虚拟继承避免重复子对象
  • 明确最终派生类对虚拟基类的初始化责任
  • 考虑使用组合和接口替代复杂的多重继承

现代C++替代方案

  • 使用模板和概念定义类型约束
  • 使用std::variant和std::visit实现类型分发
  • 使用函数对象和lambda表达式实现行为参数化
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
// 接口继承(推荐)
class Drawable {
public:
virtual void draw() const = 0;
virtual ~Drawable() = default;
};

class Serializable {
public:
virtual void serialize(std::ostream& os) const = 0;
virtual ~Serializable() = default;
};

// 多重继承接口是合理的
class Circle : public Drawable, public Serializable {
private:
double radius;
public:
explicit Circle(double r) : radius(r) {}

void draw() const override {
std::cout << "Drawing a circle with radius " << radius << std::endl;
}

void serialize(std::ostream& os) const override {
os << "Circle { radius: " << radius << " }";
}
};

6. 现代C++中的继承特性

核心特性

  • 继承构造函数(C++11+):使用using Base::Base继承基类构造函数
  • 委托构造函数(C++11+):一个构造函数可以委托给同一类的另一个构造函数
  • 继承的析构函数:基类的析构函数可以被派生类继承
  • constexpr构造函数(C++11+):允许在编译时初始化对象
  • inline命名空间(C++11+):用于版本控制和ABI兼容性

实际应用

  • 使用继承构造函数减少代码重复
  • 使用委托构造函数简化构造逻辑
  • 使用constexpr构造函数提高性能
  • 使用inline命名空间管理API版本
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
// 继承构造函数和委托构造函数
class Base {
protected:
int value;
public:
Base() : value(0) {}
Base(int v) : value(v) {}
Base(const std::string& s) : value(std::stoi(s)) {}
};

class Derived : public Base {
private:
std::string name;
public:
// 继承基类构造函数
using Base::Base;

// 委托构造函数
Derived(int v, const std::string& n) : Base(v), name(n) {}
Derived(const char* cstr) : Derived(std::string(cstr)) {}

// constexpr构造函数
constexpr Derived(int v) : Base(v), name("constexpr") {}
};

// constexpr用法
constexpr Derived d(42);
static_assert(d.value == 42, "Value should be 42");

继承的实际应用场景

1. 图形系统

核心需求:创建一个支持多种图形的系统,每种图形都有共同的行为(如绘制、计算面积)。

专家级实现

  • 使用抽象工厂模式创建图形
  • 实现原型模式支持图形克隆
  • 结合访问者模式实现图形操作
  • 优化内存布局和虚函数调用
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#include <memory>
#include <vector>
#include <string>
#include <iostream>

class Shape {
public:
virtual ~Shape() = default;
virtual void draw() const = 0;
virtual double area() const = 0;
virtual std::string name() const = 0;
virtual std::unique_ptr<Shape> clone() const = 0;
};

class Circle : public Shape {
private:
double radius;
public:
explicit Circle(double r) : radius(r) {}

void draw() const override {
std::cout << "Drawing a circle with radius " << radius << std::endl;
}

double area() const override {
return M_PI * radius * radius;
}

std::string name() const override {
return "Circle";
}

std::unique_ptr<Shape> clone() const override {
return std::make_unique<Circle>(*this);
}
};

class Rectangle : public Shape {
private:
double width, height;
public:
Rectangle(double w, double h) : width(w), height(h) {}

void draw() const override {
std::cout << "Drawing a rectangle with width " << width
<< " and height " << height << std::endl;
}

double area() const override {
return width * height;
}

std::string name() const override {
return "Rectangle";
}

std::unique_ptr<Shape> clone() const override {
return std::make_unique<Rectangle>(*this);
}
};

// 图形工厂
class ShapeFactory {
public:
static std::unique_ptr<Shape> createCircle(double radius) {
return std::make_unique<Circle>(radius);
}

static std::unique_ptr<Shape> createRectangle(double width, double height) {
return std::make_unique<Rectangle>(width, height);
}
};

// 图形处理器(访问者模式)
class ShapeProcessor {
public:
void process(const std::vector<std::unique_ptr<Shape>>& shapes) {
for (const auto& shape : shapes) {
shape->draw();
std::cout << "Area: " << shape->area() << std::endl;
}
}

std::vector<std::unique_ptr<Shape>> cloneShapes(const std::vector<std::unique_ptr<Shape>>& shapes) {
std::vector<std::unique_ptr<Shape>> clones;
clones.reserve(shapes.size());
for (const auto& shape : shapes) {
clones.push_back(shape->clone());
}
return clones;
}
};

// 使用示例
void useShapeSystem() {
std::vector<std::unique_ptr<Shape>> shapes;
shapes.push_back(ShapeFactory::createCircle(5.0));
shapes.push_back(ShapeFactory::createRectangle(4.0, 6.0));

ShapeProcessor processor;
processor.process(shapes);

auto clonedShapes = processor.cloneShapes(shapes);
std::cout << "\nCloned shapes:" << std::endl;
processor.process(clonedShapes);
}

2. 插件系统

核心需求:创建一个支持插件的系统,插件可以扩展系统的功能。

专家级实现

  • 使用动态加载(dlopen/dlsym或Windows API)
  • 实现插件生命周期管理
  • 支持插件依赖关系
  • 提供插件配置和状态管理
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#include <memory>
#include <vector>
#include <string>
#include <iostream>
#include <map>

class Plugin {
public:
virtual ~Plugin() = default;
virtual std::string getName() const = 0;
virtual std::string getVersion() const = 0;
virtual void initialize() = 0;
virtual void shutdown() = 0;
virtual void execute() = 0;
virtual bool isInitialized() const = 0;
};

class TextEditorPlugin : public Plugin {
private:
std::string pluginName;
std::string version;
bool initialized;
public:
TextEditorPlugin(const std::string& name, const std::string& ver)
: pluginName(name), version(ver), initialized(false) {}

std::string getName() const override {
return pluginName;
}

std::string getVersion() const override {
return version;
}

void initialize() override {
if (!initialized) {
std::cout << "Initializing " << pluginName << " v" << version << std::endl;
initialized = true;
}
}

void shutdown() override {
if (initialized) {
std::cout << "Shutting down " << pluginName << std::endl;
initialized = false;
}
}

void execute() override {
if (initialized) {
std::cout << "Executing " << pluginName << std::endl;
} else {
std::cerr << "Error: Plugin " << pluginName << " not initialized" << std::endl;
}
}

bool isInitialized() const override {
return initialized;
}
};

class SyntaxHighlightPlugin : public TextEditorPlugin {
public:
SyntaxHighlightPlugin() : TextEditorPlugin("Syntax Highlight", "1.0.0") {}

void execute() override {
if (isInitialized()) {
std::cout << "Applying syntax highlighting" << std::endl;
}
}
};

class AutoCompletePlugin : public TextEditorPlugin {
public:
AutoCompletePlugin() : TextEditorPlugin("Auto Complete", "1.2.0") {}

void execute() override {
if (isInitialized()) {
std::cout << "Providing auto completion suggestions" << std::endl;
}
}
};

// 插件管理器
class PluginManager {
private:
std::vector<std::unique_ptr<Plugin>> plugins;
std::map<std::string, Plugin*> pluginMap;
public:
void addPlugin(std::unique_ptr<Plugin> plugin) {
plugin->initialize();
pluginMap[plugin->getName()] = plugin.get();
plugins.push_back(std::move(plugin));
}

void executeAll() {
for (const auto& plugin : plugins) {
plugin->execute();
}
}

void executePlugin(const std::string& name) {
auto it = pluginMap.find(name);
if (it != pluginMap.end()) {
it->second->execute();
} else {
std::cerr << "Error: Plugin " << name << " not found" << std::endl;
}
}

void shutdownAll() {
for (const auto& plugin : plugins) {
plugin->shutdown();
}
}

void listPlugins() const {
std::cout << "Loaded plugins:" << std::endl;
for (const auto& plugin : plugins) {
std::cout << "- " << plugin->getName() << " v" << plugin->getVersion()
<< " (" << (plugin->isInitialized() ? "initialized" : "not initialized") << ")" << std::endl;
}
}
};

// 使用示例
void usePluginSystem() {
PluginManager manager;
manager.addPlugin(std::make_unique<SyntaxHighlightPlugin>());
manager.addPlugin(std::make_unique<AutoCompletePlugin>());

manager.listPlugins();
manager.executeAll();
manager.executePlugin("Syntax Highlight");
manager.shutdownAll();
}

3. 异常层次结构

核心需求:创建一个异常层次结构,用于不同类型的错误处理。

专家级实现

  • 继承标准异常(std::exception)
  • 实现异常上下文信息
  • 支持异常链式传递
  • 提供异常分类和错误代码
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#include <stdexcept>
#include <string>
#include <iostream>
#include <source_location>

class Exception : public std::exception {
private:
std::string message;
std::source_location location;
public:
explicit Exception(std::string msg,
const std::source_location& loc = std::source_location::current())
: message(std::move(msg)), location(loc) {}

const char* what() const noexcept override {
return message.c_str();
}

std::string getMessage() const {
return message;
}

std::string getLocation() const {
return location.file_name() + ":" + std::to_string(location.line()) + ":" + location.function_name();
}
};

class RuntimeException : public Exception {
public:
explicit RuntimeException(std::string msg,
const std::source_location& loc = std::source_location::current())
: Exception(std::move(msg), loc) {}
};

class IOException : public Exception {
private:
std::string filename;
public:
IOException(std::string msg, std::string file,
const std::source_location& loc = std::source_location::current())
: Exception(std::move(msg), loc), filename(std::move(file)) {}

const char* what() const noexcept override {
static std::string fullMessage;
fullMessage = getMessage() + " (File: " + filename + ")";
return fullMessage.c_str();
}

std::string getFilename() const {
return filename;
}
};

class FileNotFoundException : public IOException {
public:
explicit FileNotFoundException(std::string file,
const std::source_location& loc = std::source_location::current())
: IOException("File not found", std::move(file), loc) {}
};

class PermissionDeniedException : public IOException {
public:
explicit PermissionDeniedException(std::string file,
const std::source_location& loc = std::source_location::current())
: IOException("Permission denied", std::move(file), loc) {}
};

// 模拟函数
bool fileExists(const std::string& filename) {
return false; // 模拟文件不存在
}

bool hasPermission(const std::string& filename) {
return true;
}

// 使用异常层次结构
void readFile(const std::string& filename) {
if (!fileExists(filename)) {
throw FileNotFoundException(filename);
}

if (!hasPermission(filename)) {
throw PermissionDeniedException(filename);
}

// 读取文件...
}

void process() {
try {
readFile("data.txt");
} catch (const FileNotFoundException& e) {
std::cerr << "Error: " << e.what() << std::endl;
std::cerr << "Location: " << e.getLocation() << std::endl;
// 处理文件不存在的情况
} catch (const PermissionDeniedException& e) {
std::cerr << "Error: " << e.what() << std::endl;
std::cerr << "Location: " << e.getLocation() << std::endl;
// 处理权限被拒绝的情况
} catch (const Exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
std::cerr << "Location: " << e.getLocation() << std::endl;
// 处理其他异常
} catch (const std::exception& e) {
std::cerr << "Std Error: " << e.what() << std::endl;
// 处理标准异常
}
}

// 使用示例
void useExceptionSystem() {
try {
process();
} catch (...) {
std::cerr << "Unexpected error" << std::endl;
}
}

4. 事件系统

核心需求:创建一个事件系统,支持事件的发布和订阅。

专家级实现

  • 使用观察者模式
  • 支持事件过滤和优先级
  • 实现事件队列和异步处理
  • 提供事件溯源和日志
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#include <memory>
#include <vector>
#include <string>
#include <iostream>
#include <functional>
#include <map>
#include <queue>
#include <mutex>
#include <thread>
#include <condition_variable>

// 事件基类
class Event {
public:
virtual ~Event() = default;
virtual std::string getType() const = 0;
virtual bool isCancelled() const = 0;
virtual void cancel() = 0;
};

// 具体事件类
class MouseEvent : public Event {
private:
int x, y;
std::string type;
bool cancelled;
public:
MouseEvent(int xCoord, int yCoord, const std::string& eventType)
: x(xCoord), y(yCoord), type(eventType), cancelled(false) {}

std::string getType() const override {
return type;
}

bool isCancelled() const override {
return cancelled;
}

void cancel() override {
cancelled = true;
}

int getX() const { return x; }
int getY() const { return y; }
};

// 事件监听器
class EventListener {
public:
virtual ~EventListener() = default;
virtual void onEvent(const Event& event) = 0;
virtual int getPriority() const = 0;
};

// 鼠标事件监听器
class MouseEventListener : public EventListener {
private:
std::string name;
int priority;
public:
MouseEventListener(const std::string& listenerName, int prio)
: name(listenerName), priority(prio) {}

void onEvent(const Event& event) override {
if (event.getType() == "mouse_click" || event.getType() == "mouse_move") {
const MouseEvent& mouseEvent = static_cast<const MouseEvent&>(event);
std::cout << name << " received " << event.getType()
<< " event at (" << mouseEvent.getX() << ", " << mouseEvent.getY() << ")" << std::endl;
}
}

int getPriority() const override {
return priority;
}
};

// 事件总线
class EventBus {
private:
std::map<std::string, std::vector<std::unique_ptr<EventListener>>> listeners;
std::queue<std::unique_ptr<Event>> eventQueue;
std::mutex queueMutex;
std::condition_variable queueCondition;
std::thread processingThread;
bool running;

void processEvents() {
while (running) {
std::unique_ptr<Event> event;
{
std::unique_lock<std::mutex> lock(queueMutex);
queueCondition.wait(lock, [this] { return !eventQueue.empty() || !running; });
if (!running && eventQueue.empty()) break;
if (eventQueue.empty()) continue;
event = std::move(eventQueue.front());
eventQueue.pop();
}

if (event) {
dispatchEvent(*event);
}
}
}

void dispatchEvent(const Event& event) {
auto it = listeners.find(event.getType());
if (it != listeners.end()) {
// 按优先级排序
std::vector<EventListener*> sortedListeners;
for (const auto& listener : it->second) {
sortedListeners.push_back(listener.get());
}

std::sort(sortedListeners.begin(), sortedListeners.end(),
[](EventListener* a, EventListener* b) {
return a->getPriority() > b->getPriority();
});

// 分发事件
for (auto listener : sortedListeners) {
if (event.isCancelled()) break;
listener->onEvent(event);
}
}
}
public:
EventBus() : running(true), processingThread(&EventBus::processEvents, this) {}

~EventBus() {
running = false;
queueCondition.notify_one();
if (processingThread.joinable()) {
processingThread.join();
}
}

void registerListener(const std::string& eventType, std::unique_ptr<EventListener> listener) {
listeners[eventType].push_back(std::move(listener));
}

void postEvent(std::unique_ptr<Event> event) {
std::unique_lock<std::mutex> lock(queueMutex);
eventQueue.push(std::move(event));
queueCondition.notify_one();
}
};

// 使用示例
void useEventSystem() {
EventBus eventBus;

// 注册监听器
eventBus.registerListener("mouse_click", std::make_unique<MouseEventListener>("HighPriorityListener", 10));
eventBus.registerListener("mouse_click", std::make_unique<MouseEventListener>("MediumPriorityListener", 5));
eventBus.registerListener("mouse_click", std::make_unique<MouseEventListener>("LowPriorityListener", 1));
eventBus.registerListener("mouse_move", std::make_unique<MouseEventListener>("MouseMoveListener", 5));

// 发布事件
eventBus.postEvent(std::make_unique<MouseEvent>(100, 200, "mouse_click"));
eventBus.postEvent(std::make_unique<MouseEvent>(150, 250, "mouse_move"));

// 等待事件处理
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}

总结

继承是C++面向对象编程的核心特性之一,它通过代码重用、层次结构建立、类型关系定义和多态基础提供,为构建复杂的软件系统提供了强大的工具。在专家级C++开发中,继承不仅是一种代码组织方式,更是一种性能优化和系统设计的艺术。

关键技术要点

  1. 底层实现原理

    • 虚函数表(vtable)和虚函数指针(vptr)的工作机制
    • 多重继承和虚拟继承的内存布局
    • 虚函数调用的性能开销分析
    • 内存对齐和填充对继承对象大小的影响
  2. 高级继承技术

    • 协变返回类型与智能指针的结合
    • 静态多态(CRTP)替代动态多态
    • 继承构造函数和委托构造函数的优化使用
    • 虚基类的初始化和生命周期管理
  3. 性能优化策略

    • 避免在性能关键路径上使用虚函数
    • 利用编译器优化(内联、常量传播)
    • 合理使用final关键字帮助编译器优化
    • 内存布局优化减少缓存 misses
  4. 设计模式应用

    • 观察者模式实现事件系统
    • 工厂模式创建复杂对象层次
    • 原型模式支持对象克隆
    • Mixin模式组合不同行为特性
  5. 现代C++特性

    • 继承构造函数(C++11+)
    • 委托构造函数(C++11+)
    • constexpr构造函数(C++11+)
    • inline命名空间(C++11+)
    • 结构化绑定(C++17+)
  6. 最佳实践

    • 优先使用组合而非继承
    • 保持合理的继承层次深度
    • 正确使用访问控制修饰符
    • 实现多态时遵循里氏替换原则
    • 合理使用虚拟继承解决菱形继承问题

专家级开发建议

  • 性能与可维护性平衡:在性能关键路径上考虑使用静态多态,在其他地方优先考虑动态多态的可维护性
  • 接口设计:使用纯虚类定义清晰的接口,避免在接口中包含实现细节
  • 内存管理:结合智能指针和RAII原则,确保继承层次中的资源正确管理
  • 异常安全:确保构造函数和析构函数的异常安全性,避免在析构函数中抛出异常
  • 测试策略:为继承层次设计全面的单元测试,确保多态行为的正确性

通过深入理解继承的底层原理和高级应用,开发者可以在C++项目中创建更加高效、可维护和可扩展的代码结构。继承作为面向对象编程的核心特性,其正确应用对于构建大型、复杂的软件系统至关重要。