第14章 类继承 继承的概念与原理 继承是面向对象编程的核心特性之一,它允许从已有类(基类/父类)派生出新类(派生类/子类),实现代码重用和类型层次结构的建立。
继承的核心原理 代码重用(Code Reuse) :派生类自动继承基类的成员变量和成员函数,避免重复代码层次结构(Hierarchy) :建立类的层次关系,反映现实世界的分类体系类型关系(Type Relationship) :派生类与基类之间形成”is-a”关系多态基础(Polymorphism Foundation) :为运行时多态提供基础继承的语法与实现 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 class Shape {public : virtual double area () const = 0 ; virtual void draw () const = 0 ; }; 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 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 class String {private : std::vector<char > buffer; public : }; class String : private std::vector<char > {public : void push_back (char c) { std::vector<char >::push_back (c); } size_t size () const { return std::vector<char >::size (); } }; ```。 ## 构造函数与析构函数的继承 ### 构造函数的调用顺序 **核心规则**:派生类对象创建时,构造函数的调用顺序是从基类到派生类。 1. **基类构造函数**:先调用最顶层基类的构造函数2. **成员变量构造函数**:按照声明顺序调用成员变量的构造函数3. **派生类构造函数**:最后调用派生类自身的构造函数```cpp 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; public : Derived () : name ("Derived" ) { std::cout << "Derived constructor" << std::endl; } }; 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 class Base {public : virtual ~Base () { std::cout << "Base destructor" << std::endl; } }; class Derived : public Base {private : std::string name; public : ~Derived () override { std::cout << "Derived destructor" << std::endl; } }; void testDestructorOrder () { Base* ptr = new Derived (); delete ptr; }
虚析构函数的重要性 核心原则 :当使用基类指针指向派生类对象时,基类的析构函数必须声明为virtual,以确保派生类的析构函数被正确调用。
未使用虚析构函数的风险 :
只调用基类析构函数,不调用派生类析构函数 可能导致资源泄漏(如文件句柄、内存等) 程序行为未定义 构造函数的继承(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 class Base {private : 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 () : Base (), name ("Default" ) {} }; void useInheritedConstructors () { Derived d1 (42 ) ; Derived d2 ("123" ) ; Derived d3 (100 , "Custom" ) ; }
函数覆盖与隐藏 函数覆盖(Function Overriding) 核心特性 :派生类重新实现基类的虚函数,实现运行时多态。
实现条件 :
基类函数必须声明为virtual 派生类函数必须使用override关键字(C++11+) 函数签名(返回类型、函数名、参数列表)必须完全相同 返回类型可以是协变类型(covariant return type) 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 doSomething () { std::cout << "Base::doSomething()" << std::endl; } virtual Base* clone () const { return new Base (*this ); } }; class Derived : public Base {public : void doSomething () override { std::cout << "Derived::doSomething()" << std::endl; } Derived* clone () const override { return new Derived (*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 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; } }; class Derived : public Base {public : void show (const std::string& s) { std::cout << "Derived::show(string): " << s << std::endl; } using Base::show; }; void testFunctionHiding () { Derived d; d.show (42 ); d.show (3.14 ); d.show ("Hello" ); }
多重继承与虚拟继承 多重继承的概念 核心特性 :C++允许一个派生类从多个基类继承,实现更复杂的类型层次结构。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Interface1 {public : virtual void method1 () = 0 ; }; class Interface2 {public : virtual void method2 () = 0 ; }; 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; } };
菱形继承问题 核心问题 :当一个类从两个不同的路径继承同一个基类时,会产生重复的基类子对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Base {public : int value; Base () : value (42 ) {} }; class Derived1 : public Base { };class Derived2 : public Base { };class FinalDerived : public Derived1, public Derived2 { };void testDiamondProblem () { FinalDerived obj; obj.Derived1::value = 100 ; obj.Derived2::value = 200 ; }
虚拟继承(Virtual Inheritance) 核心解决方案 :使用虚拟继承解决菱形继承问题,确保最终派生类只包含一个共享的基类子对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Base {public : int value; Base () : value (42 ) {} }; class Derived1 : public virtual Base { };class Derived2 : public virtual Base { };class FinalDerived : public Derived1, public Derived2 { };void testVirtualInheritance () { FinalDerived obj; obj.value = 100 ; std::cout << "Base value: " << obj.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 class VirtualBase {public : VirtualBase (int v) : value (v) { std::cout << "VirtualBase constructor: " << value << std::endl; } int value; }; class Derived1 : public virtual VirtualBase {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; }
继承的最佳实践 1. 优先使用组合而非继承 核心原则 :当派生类与基类之间不是”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 25 26 27 class Engine {public : void start () { } void stop () { } }; class Car : public Engine { public : void drive () { } }; class Car {private : Engine engine; public : void start () { engine.start (); } void stop () { engine.stop (); } void drive () { } };
2. 合理设计继承层次 核心原则 :继承层次应该保持合理的深度,避免过深的继承链。
最佳实践 :
继承层次通常不超过3-4层 每一层都应该有明确的职责和抽象 使用接口(纯虚类)定义行为契约 避免在继承层次中混用不同的继承方式 3. 正确使用访问控制 核心原则 :根据成员的用途选择合适的访问修饰符。
最佳实践 :
public:只用于类的接口方法protected:用于需要被派生类访问的内部实现private:用于完全内部的实现细节避免使用friend关键字,除非确实必要 4. 实现多态时的注意事项 核心原则 :正确实现虚函数,确保多态行为的正确性。
最佳实践 :
基类析构函数应该声明为virtual 派生类重写虚函数时使用override关键字 避免在构造函数和析构函数中调用虚函数 考虑使用final关键字防止不必要的覆盖 5. 处理菱形继承 核心原则 :当需要多重继承时,正确使用虚拟继承解决菱形继承问题。
最佳实践 :
只在必要时使用多重继承 优先使用接口(纯虚类)作为多重继承的基类 对于有状态的基类,使用虚拟继承避免重复子对象 明确最终派生类对虚拟基类的初始化责任 继承的实际应用场景 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 class Shape {public : virtual ~Shape () = default ; virtual void draw () const = 0 ; virtual double area () const = 0 ; virtual std::string name () 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" ; } }; 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" ; } }; void processShapes (const std::vector<std::unique_ptr<Shape>>& shapes) { for (const auto & shape : shapes) { shape->draw (); std::cout << "Area: " << shape->area () << std::endl; } }
2. 插件系统 核心需求 :创建一个支持插件的系统,插件可以扩展系统的功能。
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 class Plugin {public : virtual ~Plugin () = default ; virtual std::string getName () const = 0 ; virtual void initialize () = 0 ; virtual void shutdown () = 0 ; virtual void execute () = 0 ; }; class TextEditorPlugin : public Plugin {private : std::string pluginName; public : explicit TextEditorPlugin (const std::string& name) : pluginName(name) { } std::string getName () const override { return pluginName; } void initialize () override { std::cout << "Initializing " << pluginName << std::endl; } void shutdown () override { std::cout << "Shutting down " << pluginName << std::endl; } void execute () override { std::cout << "Executing " << pluginName << std::endl; } }; class SyntaxHighlightPlugin : public TextEditorPlugin {public : SyntaxHighlightPlugin () : TextEditorPlugin ("Syntax Highlight" ) {} void execute () override { std::cout << "Applying syntax highlighting" << std::endl; } }; class AutoCompletePlugin : public TextEditorPlugin {public : AutoCompletePlugin () : TextEditorPlugin ("Auto Complete" ) {} void execute () override { std::cout << "Providing auto completion suggestions" << std::endl; } }; class PluginManager {private : std::vector<std::unique_ptr<Plugin>> plugins; public : void addPlugin (std::unique_ptr<Plugin> plugin) { plugin->initialize (); plugins.push_back (std::move (plugin)); } void executeAll () { for (const auto & plugin : plugins) { plugin->execute (); } } void shutdownAll () { for (const auto & plugin : plugins) { plugin->shutdown (); } } };
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 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 class Exception {private : std::string message; public : explicit Exception (std::string msg) : message(std::move(msg)) { } virtual ~Exception () = default ; virtual std::string what () const { return message; } }; class RuntimeException : public Exception {public : explicit RuntimeException (std::string msg) : Exception(std::move(msg)) { } }; class IOException : public Exception {private : std::string filename; public : IOException (std::string msg, std::string file) : Exception (std::move (msg)), filename (std::move (file)) {} std::string what () const override { return Exception::what () + " (File: " + filename + ")" ; } }; class FileNotFoundException : public IOException {public : explicit FileNotFoundException (std::string file) : IOException("File not found" , std::move(file)) { }}; class PermissionDeniedException : public IOException {public : explicit PermissionDeniedException (std::string file) : IOException("Permission denied" , std::move(file)) { }}; 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; } catch (const PermissionDeniedException& e) { std::cerr << "Error: " << e.what () << std::endl; } catch (const Exception& e) { std::cerr << "Error: " << e.what () << std::endl; } }
总结 继承是C++面向对象编程的核心特性之一,它通过代码重用、层次结构建立、类型关系定义和多态基础提供,为构建复杂的软件系统提供了强大的工具。
关键要点 :
正确理解和使用三种继承方式(公有、保护、私有) 掌握构造函数和析构函数的调用顺序 理解函数覆盖与隐藏的区别 正确处理多重继承和菱形继承问题 优先使用组合而非继承 遵循继承的最佳实践,保持代码的清晰性和可维护性 通过合理应用继承,开发者可以创建更加模块化、可扩展和可维护的C++代码,充分发挥面向对象编程的优势。