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

本章聚焦于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 重写
  • 派生类重写基类的虚函数,函数签名(名称、参数、返回值)必须一致。
  • 需在基类中用virtual关键字标记虚函数。

代码示例

#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 func
    delete 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 destructor
    return 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 func
    delete 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 Circle
    delete s;
    return 0;
}

6. 多态的原理

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

7. 虚函数表

  • 虚函数表(vtable):每个包含虚函数的类有一个vtable,存储虚函数地址。
  • 对象布局:对象包含一个虚表指针(vptr),指向类的vtable。
  • 动态绑定:通过vptr查找vtable中的函数地址。

代码示例(示意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 func2
    delete 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技术奠定了基础。


补充说明

  • 与哈希表章节的衔接:多态可用于哈希表的扩展设计。例如,定义一个抽象基类HashTableBase(含纯虚函数),派生出不同的哈希表实现(如链地址法、开放寻址法)。
    示例多态哈希表(结合第八章和第十五章):
    #include <iostream>
    #include <list>
    
    template<typename K, typename V>
    class HashTableBase {
    public:
        virtual void insert(K key, V value) = 0;
        virtual V find(K key) const = 0;
        virtual ~HashTableBase() = default;
    };
    
    template<typename K, typename V>
    class ChainingHashTable : public HashTableBase<K, V> {
    private:
        static const int SIZE = 10;
        std::list<std::pair<K, V>> table[SIZE];
    public:
        void insert(K key, V value) override {
            table[std::hash<K>{}(key) % SIZE].push_back({key, value});
        }
        V find(K key) const override {
            for (const auto& pair : table[std::hash<K>{}(key) % SIZE]) {
                if (pair.first == key) return pair.second;
            }
            return V();
        }
    };
    
    int main() {
        HashTableBase<int, std::string>* ht = new ChainingHashTable<int, std::string>();
        ht->insert(1, "Alice");
        std::cout << ht->find(1) << std::endl; // 输出:Alice
        delete ht;
        return 0;
    }
    
  • 进一步需求
    • 如果你需要更详细的代码实现(如虚函数表的可视化、不同哈希表实现的比较)、图表(例如vtable结构图)、或某一节的深入讲解(如动态绑定的底层机制),请告诉我。
    • 如果你有其他章节的内容需要讲解,或想结合第一章至第七章、第十五章的内容,请具体说明!

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张工在路上

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值