当前位置: 首页 > news >正文

面向对象编程(OOP)的三大特性之一(封装、继承、多态)就是第八章聚焦于C++的多态(Polymorphism),这

面向对象编程(OOP)的三大特性之一(封装、继承、多态)就是第八章聚焦于C++的多态(Polymorphism),这

本章聚焦于C++的多态(Polymorphism),这是面向对象编程(OOP)的三大特性之一(封装、继承、多态),与之前的章节(第一章:C++基础、第二章:类和对象(上)、第三章:类和对象(下)、第四章:内存管理、第五章:模板、第六章:STL、第七章:继承、第十五章:哈希表)紧密相关。多态为实现灵活、可扩展的代码提供了支持,尤其在设计复杂数据结构(如哈希表)时非常有用。简要概述每个部分的内容,提供简洁的C++代码示例,并保持与之前学习的章节衔接。


前言

多态允许同一接口在不同对象上表现出不同行为,是C++面向对象编程的核心特性。本章介绍多态的实现、虚函数、抽象类、虚函数表等内容,为实现动态行为和设计模式奠定基础,同时与哈希表等数据结构的扩展性设计相关。


1. 多态的概念


2. 多态的实现

运行期多态通过继承(第七章)和虚函数实现:

  • 基类定义虚函数,派生类重写(override)以提供特定实现。
  • 通过基类指针或引用调用虚函数,动态绑定到实际对象类型。

代码示例

#include <iostream>class Base {public:virtual void print() { std::cout << "Base" << std::endl; } // 虚函数};class Derived : public Base {public:void print() override { std::cout << "Derived" << std::endl; } // 重写};int main() {Base* b = new Derived(); // 基类指针指向派生类b->print(); // 输出:Derived(动态绑定)delete b;return 0;}

3. 虚函数的重写

3.1 重写

代码示例

#include <iostream>class Base {public:virtual void func() { std::cout << "Base func" << std::endl; }};class Derived : public Base {public:void func() override { std::cout << "Derived func" << std::endl; }};int main() {Base* b = new Derived();b->func(); // 输出:Derived funcdelete b;return 0;}
3.2 协变

协变允许派生类重写虚函数时,返回类型是基类返回类型的派生类(通常用于指针或引用)。

代码示例

#include <iostream>class Base {public:virtual Base* clone() { return new Base(); }};class Derived : public Base {public:Derived* clone() override { return new Derived(); } // 协变返回类型};int main() {Base* b = new Derived();Base* c = b->clone(); // 返回Derived*,但安全转换为Base*delete b;delete c;return 0;}
3.3 析构函数的重写
  • 基类析构函数应声明为virtual,否则通过基类指针删除派生类对象时,只调用基类析构函数,可能导致资源泄漏。
  • 派生类析构函数自动重写基类的虚析构函数。

代码示例

#include <iostream>class Base {public:virtual ~Base() { std::cout << "Base destructor" << std::endl; }};class Derived : public Base {public:~Derived() override { std::cout << "Derived destructor" << std::endl; }};int main() {Base* b = new Derived();delete b; // 输出:Derived destructor, Base destructorreturn 0;}
3.4 override和final关键字
  • override(C++11):显式声明函数重写基类虚函数,确保签名匹配。
  • final(C++11):禁止派生类进一步重写虚函数或继承类。

代码示例

#include <iostream>class Base {public:virtual void func() { std::cout << "Base func" << std::endl; }};class Derived : public Base {public:void func() override final { std::cout << "Derived func" << std::endl; } // final禁止重写};class SubDerived : public Derived {// void func() override; // 错误:func是final};int main() {Base* b = new Derived();b->func(); // 输出:Derived funcdelete b;return 0;}
3.5 重载、重写和隐藏的对比(重点)
特性重载(Overload)重写(Override)隐藏(Hiding)
定义同作用域,函数名相同,参数不同继承关系,虚函数签名一致派生类同名函数隐藏基类函数
关键字virtual/override
作用域同一类或命名空间基类与派生类基类与派生类
绑定编译期运行期(动态绑定)编译期

代码示例

#include <iostream>class Base {public:virtual void func() { std::cout << "Base func" << std::endl; } // 虚函数void func(int x) { std::cout << "Base func with int: " << x << std::endl; } // 重载};class Derived : public Base {public:void func() override { std::cout << "Derived func" << std::endl; } // 重写void func(double x) { std::cout << "Derived func with double: " << x << std::endl; } // 隐藏};int main() {Base* b = new Derived();b->func(); // 输出:Derived func(重写)// b->func(3.14); // 错误:Base无func(double)Derived d;d.func(3.14); // 输出:Derived func with double: 3.14(隐藏)d.Base::func(5); // 输出:Base func with int: 5(访问隐藏的基类函数)delete b;return 0;}

5. 纯虚函数和抽象类

  • 纯虚函数:声明为virtual void func() = 0;,无实现,派生类必须重写。
  • 抽象类:包含至少一个纯虚函数的类,不能实例化,常用作接口。

代码示例

#include <iostream>class Shape {public:virtual void draw() = 0; // 纯虚函数virtual ~Shape() = default; // 虚析构函数};class Circle : public Shape {public:void draw() override { std::cout << "Draw Circle" << std::endl; }};int main() {// Shape s; // 错误:抽象类不可实例化Shape* s = new Circle();s->draw(); // 输出:Draw Circledelete s;return 0;}

6. 多态的原理

  • 运行期多态通过**虚函数表(vtable)**实现。
  • 基类指针或引用调用虚函数时,根据对象实际类型的vtable查找函数地址。
  • 虚函数调用有少量运行时开销,但提供动态行为。

7. 虚函数表

代码示例(示意vtable行为):

#include <iostream>class Base {public:virtual void func1() { std::cout << "Base func1" << std::endl; }virtual void func2() { std::cout << "Base func2" << std::endl; }};class Derived : public Base {public:void func1() override { std::cout << "Derived func1" << std::endl; }};int main() {Base* b = new Derived();b->func1(); // 输出:Derived func1(通过vtable调用)b->func2(); // 输出:Base func2delete b;return 0;}

8. 动态绑定与静态绑定

  • 动态绑定:运行期通过vtable确定虚函数调用,适用于多态。
  • 静态绑定:编译期确定函数调用,适用于非虚函数或直接调用。
  • 区别
    • 动态绑定:基类指针/引用调用虚函数,运行时解析。
    • 静态绑定:函数调用在编译时确定,效率更高。

代码示例

#include <iostream>class Base {public:virtual void dynamicFunc() { std::cout << "Base dynamic" << std::endl; }void staticFunc() { std::cout << "Base static" << std::endl; }};class Derived : public Base {public:void dynamicFunc() override { std::cout << "Derived dynamic" << std::endl; }void staticFunc() { std::cout << "Derived static" << std::endl; }};int main() {Base* b = new Derived();b->dynamicFunc(); // 输出:Derived dynamic(动态绑定)b->staticFunc();  // 输出:Base static(静态绑定)delete b;return 0;}

尾声

多态通过虚函数和动态绑定实现了灵活的行为切换,是C++面向对象编程的核心。本章内容为实现复杂数据结构(如哈希表)提供了扩展性支持,同时为后续学习设计模式和高级OOP技术奠定了基础。


补充说明

希望这部分解答对你学习C++多态有帮助! 如果有任何问题,随时告诉我!