第16章 类的多态 多态的概念 多态是面向对象编程的三大核心特性之一,它允许不同类型的对象对同一消息做出不同的响应。多态的本质是”一个接口,多种实现”。
多态的实现方式 编译时多态 :通过函数重载和运算符重载实现运行时多态 :通过虚函数和继承实现虚函数 虚函数是实现运行时多态的基础,它允许在派生类中重新定义基类的方法。
虚函数的声明 1 2 3 4 5 6 class Base {public : virtual void show () { std::cout << "Base class show()" << std::endl; } };
虚函数的重写 1 2 3 4 5 6 class Derived : public Base {public : void show () override { std::cout << "Derived class show()" << std::endl; } };
虚析构函数 当使用基类指针指向派生类对象时,为了确保正确调用派生类的析构函数,基类的析构函数应该声明为虚函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 class Base {public : virtual ~Base () { std::cout << "Base destructor" << std::endl; } }; class Derived : public Base {public : ~Derived () { std::cout << "Derived destructor" << std::endl; } };
抽象类和纯虚函数 抽象类是不能实例化的类,它通常包含一个或多个纯虚函数。
纯虚函数的声明 1 2 3 4 class AbstractBase {public : virtual void pureVirtual () = 0 ; };
抽象类的继承 派生类必须实现基类中的所有纯虚函数,否则派生类也会成为抽象类。
1 2 3 4 5 6 class ConcreteDerived : public AbstractBase {public : void pureVirtual () override { std::cout << "Implemented pure virtual function" << std::endl; } };
接口类 在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 class Shape {public : virtual double area () const = 0 ; virtual double perimeter () const = 0 ; virtual ~Shape () = default ; }; class Circle : public Shape {private : double radius; public : Circle (double r) : radius (r) {} double area () const override { return M_PI * radius * radius; } double perimeter () const override { return 2 * M_PI * radius; } }; class Rectangle : public Shape {private : double width, height; public : Rectangle (double w, double h) : width (w), height (h) {} double area () const override { return width * height; } double perimeter () const override { return 2 * (width + height); } };
多态的应用 多态的使用场景 统一接口 :通过基类指针或引用调用派生类的方法扩展系统 :添加新的派生类而不需要修改现有代码代码复用 :通过继承和多态实现代码的复用多态的实现原理 C++通过虚函数表(vtable)和虚指针(vptr)实现运行时多态:
每个包含虚函数的类都有一个虚函数表 每个类的对象都包含一个指向虚函数表的指针(虚指针) 当调用虚函数时,通过虚指针找到虚函数表,再根据函数在表中的位置调用相应的函数 多态与类型转换 向上转换 向上转换是将派生类指针或引用转换为基类指针或引用,这是安全的隐式转换。
1 2 Derived derived; Base* basePtr = &derived;
向下转换 向下转换是将基类指针或引用转换为派生类指针或引用,需要使用dynamic_cast进行安全转换。
1 2 3 4 5 6 Base* basePtr = new Derived (); Derived* derivedPtr = dynamic_cast <Derived*>(basePtr); if (derivedPtr) { derivedPtr->derivedMethod (); }
多态的局限性 性能开销 :虚函数调用比普通函数调用慢内存开销 :每个对象需要额外的虚指针复杂性 :增加了代码的复杂性和理解难度多态的最佳实践 合理使用虚函数 :只在需要多态的地方使用虚函数使用override关键字 :明确表示重写基类的虚函数使用final关键字 :防止虚函数被进一步重写合理设计类层次 :避免过深的继承层次现代C++中的多态特性 final关键字的使用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 class FinalClass final {public : virtual void method () { std::cout << "FinalClass::method()" << std::endl; } }; class Base {public : virtual void method () final { std::cout << "Base::method()" << std::endl; } }; class Derived : public Base {public : };
虚函数的默认参数 虚函数的默认参数是静态绑定的,即使用的是基类中定义的默认参数值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Base {public : virtual void method (int value = 10 ) { std::cout << "Base::method(" << value << ")" << std::endl; } }; class Derived : public Base {public : void method (int value = 20 ) override { std::cout << "Derived::method(" << value << ")" << std::endl; } }; int main () { Base* basePtr = new Derived (); basePtr->method (); delete basePtr; return 0 ; }
纯虚函数的默认实现(C++11+) C++11允许为纯虚函数提供默认实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Base {public : virtual void pureVirtual () = 0 ; }; void Base::pureVirtual () { std::cout << "Base::pureVirtual() default implementation" << std::endl; } class Derived : public Base {public : void pureVirtual () override { Base::pureVirtual (); std::cout << "Derived::pureVirtual()" << 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 #include <memory> class Shape {public : virtual void draw () const = 0 ; virtual ~Shape () = default ; }; class Circle : public Shape {public : void draw () const override { std::cout << "Drawing a circle" << std::endl; } }; class Rectangle : public Shape {public : void draw () const override { std::cout << "Drawing a rectangle" << std::endl; } }; int main () { std::vector<std::unique_ptr<Shape>> shapes; shapes.push_back (std::make_unique <Circle>()); shapes.push_back (std::make_unique <Rectangle>()); for (const auto & shape : shapes) { shape->draw (); } return 0 ; }
示例:图形系统 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 #include <iostream> #include <memory> #include <vector> class Shape {public : virtual void draw () const = 0 ; virtual ~Shape () = default ; }; class Circle : public Shape {private : int x, y, radius; public : Circle (int x, int y, int radius) : x (x), y (y), radius (radius) {} void draw () const override { std::cout << "Drawing Circle at (" << x << ", " << y << ") with radius " << radius << std::endl; } }; class Rectangle : public Shape {private : int x, y, width, height; public : Rectangle (int x, int y, int width, int height) : x (x), y (y), width (width), height (height) {} void draw () const override { std::cout << "Drawing Rectangle at (" << x << ", " << y << ") with width " << width << " and height " << height << std::endl; } }; int main () { std::vector<std::unique_ptr<Shape>> shapes; shapes.push_back (std::make_unique <Circle>(10 , 10 , 5 )); shapes.push_back (std::make_unique <Rectangle>(20 , 20 , 10 , 8 )); for (const auto & shape : shapes) { shape->draw (); } return 0 ; }
总结 多态是C++面向对象编程的重要特性,它通过虚函数机制实现了运行时的动态绑定,使得代码更加灵活和可扩展。合理使用多态可以提高代码的可读性、可维护性和可扩展性,是现代C++编程中的重要技术之一。