第14章 类继承
继承的概念与原理
继承是面向对象编程的核心特性之一,它允许从已有类(基类/父类)派生出新类(派生类/子类),实现代码重用和类型层次结构的建立。在专家级C++开发中,继承不仅是代码组织工具,更是性能优化和系统设计的关键技术。
继承的核心原理
- 代码重用(Code Reuse):派生类自动继承基类的成员变量和成员函数,避免重复代码
- 层次结构(Hierarchy):建立类的层次关系,反映现实世界的分类体系
- 类型关系(Type Relationship):派生类与基类之间形成”is-a”关系
- 多态基础(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; };
|
虚函数调用的性能开销
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| void benchmarkVirtualCalls() { Base* basePtr = new Derived(); 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() {} };
|
内存对齐优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class BadLayout { public: char c; virtual void foo(); int i; };
class GoodLayout { public: virtual void foo(); int i; char 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 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成员在派生类中仍然是public,protected成员仍然是protected,private成员不可访问。
应用场景:体现”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; } };
|
保护继承(protected inheritance)
核心特性:基类的public和protected成员在派生类中都变为protected,private成员不可访问。
应用场景:体现”is-implemented-in-terms-of”关系,用于实现细节的重用。
底层实现:保护继承的内存布局与公有继承相同,但编译器会限制对基类成员的访问权限。
私有继承(private inheritance)
核心特性:基类的public和protected成员在派生类中都变为private,private成员不可访问。
应用场景:体现”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; } };
|
继承方式的性能影响
| 继承方式 | 内存开销 | 访问速度 | 代码复杂度 | 适用场景 |
|---|
| 公有继承 | 基类大小 + 派生类大小 | 直接访问 | 低 | is-a关系 |
| 保护继承 | 基类大小 + 派生类大小 | 直接访问 | 中 | 实现细节重用 |
| 私有继承 | 基类大小 + 派生类大小(可能有EBO优化) | 直接访问 | 高 | 完全隐藏实现 |
| 组合 | 成员对象大小 + 派生类大小 | 间接访问(通过成员) | 低 | has-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 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); } };
void testConstructorOrder() { Derived d; }
|
析构函数的调用顺序
核心规则:派生类对象销毁时,析构函数的调用顺序与构造函数相反。
- 派生类析构函数:先调用派生类自身的析构函数
- 成员变量析构函数:按照声明顺序的逆序调用成员变量的析构函数
- 基类析构函数:最后调用基类的析构函数
异常处理:析构函数不应该抛出异常,否则可能导致程序终止。
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 { 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 { std::cout << "Derived destructor" << std::endl; } };
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); Derived d2("123"); Derived d3(100, "Custom"); Derived d4(d3); Derived d5(std::move(d4)); }
|
继承构造函数的限制
- 默认构造函数:基类的默认构造函数不会被继承,需要显式定义
- 拷贝/移动构造函数:基类的拷贝和移动构造函数不会被继承
- 构造函数冲突:如果派生类有与基类构造函数签名相同的构造函数,继承的构造函数会被隐藏
- 访问控制:继承的构造函数保持基类的访问级别
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; };
void testInheritedConstructorAccess() { Derived d1; }
|
函数覆盖与隐藏
函数覆盖(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* clone() const override { return new Derived(*this); } virtual void finalMethod() final { std::cout << "Derived::finalMethod()" << std::endl; } };
|
协变返回类型的高级用法
核心特性:协变返回类型允许派生类的覆盖函数返回比基类函数更具体的类型。
高级应用:
- 与智能指针结合使用
- 与模板类结合使用
- 多级继承中的协变返回类型
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: std::unique_ptr<Derived> create() const override { return std::make_unique<Derived>(); } Derived* clone() const override { return new Derived(*this); } };
class MoreDerived : public Derived { public: std::unique_ptr<MoreDerived> create() const override { return std::make_unique<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: void show(const std::string& s) { std::cout << "Derived::show(string): " << s << std::endl; } void display() { std::cout << "Derived::display()" << std::endl; } using Base::show; };
void testFunctionHiding() { Derived d; d.show(42); d.show(3.14); d.show("Hello"); Base* basePtr = &d; basePtr->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
| 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; } };
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; };
|
菱形继承问题
核心问题:当一个类从两个不同的路径继承同一个基类时,会产生重复的基类子对象。
深度分析:
- 内存浪费:每个路径都有一个基类子对象
- 数据不一致:不同路径的基类子对象可能有不同的值
- 访问歧义:直接访问基类成员会产生编译错误
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; };
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; };
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 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; } };
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
| class Engine { public: virtual void start() { } virtual void stop() { } virtual ~Engine() = default; };
class Car : public Engine { public: void drive() { } };
class Car { private: std::unique_ptr<Engine> 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]]标记重要的返回值 - 使用
constexpr和consteval提高性能 - 使用
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: class Impl; std::unique_ptr<Impl> impl; public: Widget(); ~Widget(); Widget(Widget&&) noexcept; Widget& operator=(Widget&&) noexcept; void doSomething(); [[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() { } virtual ~Base() { } 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; } 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 Derived(int v) : Base(v), name("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++开发中,继承不仅是一种代码组织方式,更是一种性能优化和系统设计的艺术。
关键技术要点:
底层实现原理
- 虚函数表(vtable)和虚函数指针(vptr)的工作机制
- 多重继承和虚拟继承的内存布局
- 虚函数调用的性能开销分析
- 内存对齐和填充对继承对象大小的影响
高级继承技术
- 协变返回类型与智能指针的结合
- 静态多态(CRTP)替代动态多态
- 继承构造函数和委托构造函数的优化使用
- 虚基类的初始化和生命周期管理
性能优化策略
- 避免在性能关键路径上使用虚函数
- 利用编译器优化(内联、常量传播)
- 合理使用final关键字帮助编译器优化
- 内存布局优化减少缓存 misses
设计模式应用
- 观察者模式实现事件系统
- 工厂模式创建复杂对象层次
- 原型模式支持对象克隆
- Mixin模式组合不同行为特性
现代C++特性
- 继承构造函数(C++11+)
- 委托构造函数(C++11+)
- constexpr构造函数(C++11+)
- inline命名空间(C++11+)
- 结构化绑定(C++17+)
最佳实践
- 优先使用组合而非继承
- 保持合理的继承层次深度
- 正确使用访问控制修饰符
- 实现多态时遵循里氏替换原则
- 合理使用虚拟继承解决菱形继承问题
专家级开发建议:
- 性能与可维护性平衡:在性能关键路径上考虑使用静态多态,在其他地方优先考虑动态多态的可维护性
- 接口设计:使用纯虚类定义清晰的接口,避免在接口中包含实现细节
- 内存管理:结合智能指针和RAII原则,确保继承层次中的资源正确管理
- 异常安全:确保构造函数和析构函数的异常安全性,避免在析构函数中抛出异常
- 测试策略:为继承层次设计全面的单元测试,确保多态行为的正确性
通过深入理解继承的底层原理和高级应用,开发者可以在C++项目中创建更加高效、可维护和可扩展的代码结构。继承作为面向对象编程的核心特性,其正确应用对于构建大型、复杂的软件系统至关重要。