本章聚焦于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++多态有帮助!😊 如果有任何问题,随时告诉我!