1. 多态与公有继承的核心概念
在C++面向对象编程中,多态性是最强大的特性之一。它允许我们通过基类指针或引用调用派生类的成员函数,实现"一个接口,多种实现"的效果。公有继承是实现多态的基础机制,它建立了is-a关系,使得派生类对象可以被当作基类对象使用。
多态的实现依赖于两个关键技术点:
- 虚函数(virtual function):通过在基类中声明虚函数,派生类可以重写这些函数
- 动态绑定(dynamic binding):在运行时根据对象的实际类型决定调用哪个函数版本
我在实际项目中发现,正确理解和使用多态可以大幅提升代码的可扩展性和可维护性。特别是在开发框架或库时,多态允许用户通过继承和重写来扩展功能,而不需要修改原有代码。
2. 公有继承中的多态实现机制
2.1 虚函数表的工作原理
每个包含虚函数的类都有一个虚函数表(vtable),这是一个编译器自动生成的函数指针数组。当对象被创建时,编译器会在对象内存布局的最前面添加一个指向vtable的指针(vptr)。
cpp复制class Shape {
public:
virtual void draw() const = 0;
virtual double area() const = 0;
};
class Circle : public Shape {
public:
void draw() const override { /* 实现 */ }
double area() const override { /* 实现 */ }
};
在这个例子中:
- Shape类有自己的vtable,包含draw和area的函数指针
- Circle类也有自己的vtable,包含重写后的函数实现
- 当创建Circle对象时,它的vptr会指向Circle的vtable
注意:虚函数调用比普通函数调用多一次间接寻址操作,会有轻微的性能开销。在性能敏感的代码中需要权衡使用。
2.2 override和final关键字的最佳实践
C++11引入了override和final关键字来更安全地使用多态:
cpp复制class Base {
public:
virtual void foo(int);
virtual void bar() const final; // 禁止派生类重写
};
class Derived : public Base {
public:
void foo(int) override; // 明确表示要重写基类虚函数
// void bar() const; // 错误!不能重写final函数
};
使用override的好处:
- 编译器会检查是否真的重写了基类的虚函数
- 防止因函数签名不匹配导致的意外隐藏而非重写
- 提高代码可读性,明确表达设计意图
3. 多态在实际项目中的应用模式
3.1 工厂方法模式
多态最常见的应用场景之一是工厂模式。通过基类接口创建具体派生类对象:
cpp复制class Document {
public:
virtual void open() = 0;
virtual void save() = 0;
};
class TextDocument : public Document {
void open() override { /* 文本文件打开实现 */ }
void save() override { /* 文本文件保存实现 */ }
};
class SpreadsheetDocument : public Document {
void open() override { /* 电子表格打开实现 */ }
void save() override { /* 电子表格保存实现 */ }
};
Document* createDocument(const std::string& type) {
if (type == "text") return new TextDocument();
if (type == "spreadsheet") return new SpreadsheetDocument();
return nullptr;
}
这种设计的好处是:
- 新增文档类型只需添加新的派生类,不需要修改工厂函数以外的代码
- 客户端代码通过统一的Document接口操作各种文档
- 符合开闭原则(对扩展开放,对修改关闭)
3.2 策略模式
多态也常用于实现策略模式,将算法封装为可互换的对象:
cpp复制class SortingStrategy {
public:
virtual void sort(std::vector<int>& data) = 0;
};
class QuickSort : public SortingStrategy {
void sort(std::vector<int>& data) override { /* 快速排序实现 */ }
};
class MergeSort : public SortingStrategy {
void sort(std::vector<int>& data) override { /* 归并排序实现 */ }
};
class Sorter {
SortingStrategy* strategy;
public:
void setStrategy(SortingStrategy* s) { strategy = s; }
void execute(std::vector<int>& data) { strategy->sort(data); }
};
4. 多态使用中的常见陷阱与解决方案
4.1 对象切片问题
当派生类对象通过值传递方式赋给基类对象时,会发生对象切片(object slicing):
cpp复制class Base { /* 有虚函数 */ };
class Derived : public Base { /* 添加新成员 */ };
void func(Base b) { /* ... */ }
Derived d;
func(d); // 只复制了Base部分,Derived特有部分被"切片"掉了
解决方案:
- 始终通过指针或引用传递多态对象
- 考虑使用智能指针管理对象生命周期
4.2 虚析构函数必要性
如果基类有虚函数但没有虚析构函数,通过基类指针删除派生类对象会导致未定义行为:
cpp复制class Base {
public:
// virtual ~Base() = default; // 错误!缺少虚析构函数
virtual void foo();
};
class Derived : public Base { /* 可能有自己的资源 */ };
Base* p = new Derived();
delete p; // 未定义行为,Derived的析构函数不会被调用
最佳实践:
- 如果一个类有任何虚函数,它就应该有虚析构函数
- 对于设计为基类的类,即使没有虚函数,也应考虑将析构函数声明为virtual
4.3 多重继承的复杂性
C++支持多重继承,但结合多态使用时需要格外小心:
cpp复制class A { virtual void foo(); };
class B { virtual void foo(); };
class C : public A, public B {
void foo() override; // 需要明确重写哪个基类的foo
};
多重继承的常见问题:
- 菱形继承导致的二义性
- 虚基类的初始化顺序复杂性
- 类型转换时的指针调整
建议:
- 优先使用单一继承
- 如果必须使用多重继承,考虑使用接口类(纯虚类)
- 明确使用作用域解析符解决二义性
5. 性能考量与优化技巧
5.1 虚函数调用开销分析
虚函数调用比普通函数调用多出以下开销:
- 通过vptr找到vtable(一次指针解引用)
- 通过vtable找到函数地址(二次指针解引用)
- 可能影响内联优化
实测数据(仅供参考):
- 普通函数调用:约1-3个时钟周期
- 虚函数调用:约5-10个时钟周期
优化建议:
- 在性能关键路径上避免频繁的虚函数调用
- 考虑使用CRTP(奇异递归模板模式)实现编译时多态
- 对于小型函数,权衡是否真的需要多态
5.2 虚函数的内联可能性
虚函数通常不能内联,因为调用哪个函数在运行时才能确定。但有一种特殊情况:
cpp复制class Base {
public:
virtual void foo() { /* 实现 */ }
};
class Derived : public Base {
void foo() override { /* 不同实现 */ }
};
void callFoo(Base& b) {
b.foo(); // 通常不能内联
}
void callFooOnDerived() {
Derived d;
d.foo(); // 可能被内联,因为对象的具体类型在编译时已知
}
6. 现代C++中的多态新特性
6.1 override和final的深入使用
override和final除了基本用法外,还有一些高级应用场景:
cpp复制class Interface {
public:
virtual void foo() = 0;
virtual void bar() = 0;
};
class AbstractBase : public Interface {
public:
void foo() override final { /* 提供默认实现并禁止进一步重写 */ }
// bar()保持纯虚,需要派生类实现
};
class Concrete : public AbstractBase {
public:
void bar() override { /* 必须实现 */ }
// 不能重写foo()
};
这种设计模式:
- 允许基类提供部分默认实现
- 精确控制哪些函数可以被派生类重写
- 提高接口的明确性和安全性
6.2 使用std::variant和std::visit实现多态
C++17引入了新的方式实现类似多态的行为:
cpp复制class Circle { void draw() const; };
class Square { void draw() const; };
using Shape = std::variant<Circle, Square>;
void drawShape(const Shape& s) {
std::visit([](const auto& shape) { shape.draw(); }, s);
}
这种方式的优缺点:
- 优点:不需要继承体系,值语义,可能更好的性能
- 缺点:类型集合必须预先知道,扩展需要修改variant定义
7. 多态在大型项目中的架构设计
7.1 插件系统设计
多态是实现插件系统的理想选择:
cpp复制// 框架头文件
class Plugin {
public:
virtual ~Plugin() = default;
virtual void initialize() = 0;
virtual void execute() = 0;
};
using PluginCreator = Plugin* (*)();
// 插件实现
extern "C" {
Plugin* createPlugin() {
return new MyPlugin();
}
}
class MyPlugin : public Plugin {
void initialize() override { /* ... */ }
void execute() override { /* ... */ }
};
关键设计点:
- 定义清晰的插件接口
- 使用C链接规范确保跨编译器兼容性
- 提供插件注册和发现机制
- 考虑使用智能指针管理插件生命周期
7.2 跨模块边界使用多态
在DLL/SO等动态库中使用多态需要特别注意:
-
虚函数表布局必须一致:
- 使用相同编译器版本
- 保持相同的编译选项
- 考虑使用接口类(纯虚类)
-
内存管理边界:
- 谁分配谁释放原则
- 考虑使用工厂函数返回智能指针
cpp复制// 跨模块接口设计示例
class IModuleInterface {
public:
virtual void performTask() = 0;
virtual ~IModuleInterface() = default;
};
// 导出的工厂函数
extern "C" std::unique_ptr<IModuleInterface> createModule();
8. 测试多态代码的最佳实践
8.1 模拟对象测试
多态使得使用模拟对象(Mock)进行单元测试变得容易:
cpp复制class Database {
public:
virtual ~Database() = default;
virtual std::string query(const std::string&) = 0;
};
class MockDatabase : public Database {
public:
MOCK_METHOD(std::string, query, (const std::string&), (override));
};
TEST(MyTest, DatabaseTest) {
MockDatabase mock;
EXPECT_CALL(mock, query("test")).WillOnce(Return("result"));
// 测试使用Database接口的代码
}
使用Google Mock等框架可以:
- 验证接口调用是否符合预期
- 模拟各种返回值和异常情况
- 测试难以复现的边缘情况
8.2 多态代码的覆盖率分析
确保多态代码的充分测试需要:
- 为每个派生类编写测试用例
- 测试基类指针指向各种派生类的情况
- 检查所有虚函数的重写版本
- 验证资源管理是否正确(特别是析构)
工具建议:
- gcov/lcov生成覆盖率报告
- Clang的源码覆盖率工具
- 商业工具如Coverity
9. 多态与其它面向对象特性的协同
9.1 多态与RAII的结合
资源获取即初始化(RAII)是多态对象管理资源的理想方式:
cpp复制class ResourceHandler {
public:
virtual ~ResourceHandler() = default;
virtual void useResource() = 0;
};
class FileHandler : public ResourceHandler {
std::fstream file;
public:
explicit FileHandler(const std::string& path) : file(path) {}
void useResource() override { /* 文件操作 */ }
~FileHandler() override { file.close(); }
};
void processResource(std::unique_ptr<ResourceHandler> handler) {
handler->useResource();
// 资源自动释放
}
这种模式确保了:
- 资源在对象生命周期内有效
- 资源在对象销毁时自动释放
- 多态调用可以处理各种资源类型
9.2 多态与移动语义
C++11引入的移动语义可以与多态结合使用:
cpp复制class PolymorphicObject {
public:
virtual ~PolymorphicObject() = default;
virtual std::unique_ptr<PolymorphicObject> clone() const = 0;
virtual void doWork() = 0;
};
class ConcreteObject : public PolymorphicObject {
std::vector<int> data;
public:
std::unique_ptr<PolymorphicObject> clone() const override {
return std::make_unique<ConcreteObject>(*this);
}
void doWork() override { /* 使用data */ }
// 移动操作
ConcreteObject(ConcreteObject&&) = default;
ConcreteObject& operator=(ConcreteObject&&) = default;
};
设计考虑:
- 多态对象通常通过指针传递,移动语义主要用在实现内部
- clone模式是复制多态对象的常用技术
- 移动操作可以提升包含多态成员对象的性能
10. 从设计模式看多态的应用
10.1 观察者模式中的多态
观察者模式是展示多态威力的经典案例:
cpp复制class Observer {
public:
virtual ~Observer() = default;
virtual void update(const std::string& message) = 0;
};
class Subject {
std::vector<Observer*> observers;
public:
void attach(Observer* o) { observers.push_back(o); }
void notify(const std::string& msg) {
for (auto o : observers) o->update(msg);
}
};
class Logger : public Observer {
void update(const std::string& msg) override {
std::cout << "Log: " << msg << std::endl;
}
};
class Alert : public Observer {
void update(const std::string& msg) override {
if (msg.find("error") != std::string::npos) {
std::cerr << "ALERT: " << msg << std::endl;
}
}
};
这种设计允许:
- 动态添加和移除观察者
- 各种观察者以不同方式响应通知
- 主题不需要知道具体观察者类型
10.2 装饰器模式中的多态
装饰器模式通过多态动态扩展对象功能:
cpp复制class Stream {
public:
virtual ~Stream() = default;
virtual void write(const std::string&) = 0;
};
class FileStream : public Stream {
void write(const std::string& data) override {
// 写入文件
}
};
class DecoratorStream : public Stream {
Stream* wrapped;
public:
explicit DecoratorStream(Stream* s) : wrapped(s) {}
void write(const std::string& data) override {
wrapped->write(data);
}
};
class CompressingStream : public DecoratorStream {
public:
using DecoratorStream::DecoratorStream;
void write(const std::string& data) override {
auto compressed = compress(data);
DecoratorStream::write(compressed);
}
};
class EncryptingStream : public DecoratorStream {
public:
using DecoratorStream::DecoratorStream;
void write(const std::string& data) override {
auto encrypted = encrypt(data);
DecoratorStream::write(encrypted);
}
};
使用方式:
cpp复制Stream* stream = new FileStream();
stream = new CompressingStream(stream); // 添加压缩功能
stream = new EncryptingStream(stream); // 添加加密功能
stream->write("data"); // 数据会被压缩、加密后写入文件
装饰器模式的优势:
- 运行时动态添加功能
- 避免子类爆炸问题
- 功能可以任意组合
11. 多态在标准库中的应用实例
11.1 标准流的多态设计
C++标准库中的流类是多态设计的典范:
cpp复制class basic_ios { /* 虚析构函数等 */ };
class basic_istream : virtual public basic_ios { /* 输入操作 */ };
class basic_ostream : virtual public basic_ios { /* 输出操作 */ };
class basic_iostream : public basic_istream, public basic_ostream { /* 输入输出 */ };
// 实际使用
std::ostream& operator<<(std::ostream& os, const MyType& obj) {
// 可以接受任何派生自ostream的流
return os << obj.toString();
}
设计特点:
- 使用虚继承解决菱形继承问题
- 通过多态支持各种流设备(文件、内存、字符串等)
- 用户自定义类型可以通过重载<<与所有流兼容
11.2 异常处理中的多态
C++异常处理也利用了多态:
cpp复制class std::exception {
public:
virtual ~exception();
virtual const char* what() const noexcept;
};
class MyException : public std::exception {
const char* what() const noexcept override {
return "My custom exception";
}
};
try {
throw MyException();
} catch (const std::exception& e) {
std::cout << e.what() << std::endl; // 多态调用
}
这种设计允许:
- 捕获异常时不需要知道具体类型
- 异常层次结构可以自由扩展
- 统一的异常处理接口
12. 多态与模板的对比与结合
12.1 静态多态与动态多态
C++支持两种多态方式:
-
动态多态(运行时多态):
- 基于继承和虚函数
- 运行时决定调用哪个函数
- 灵活性高,有运行时开销
-
静态多态(编译时多态):
- 基于模板和函数重载
- 编译时决定调用哪个函数
- 性能好,灵活性受限
cpp复制// 动态多态
class Animal {
public:
virtual void speak() const = 0;
};
// 静态多态
template <typename T>
void makeSpeak(const T& animal) {
animal.speak();
}
12.2 CRTP模式
奇异递归模板模式(CRTP)结合了继承和模板:
cpp复制template <typename Derived>
class Base {
public:
void interface() {
static_cast<Derived*>(this)->implementation();
}
};
class Derived : public Base<Derived> {
public:
void implementation() {
// 具体实现
}
};
CRTP的特点:
- 编译时多态,无虚函数开销
- 基类可以访问派生类的成员
- 常用于实现静态多态和mixins
13. 多态代码的调试技巧
13.1 调试虚函数调用
调试多态代码时的一些技巧:
- 在调试器中查看对象的vptr和vtable
- 设置断点在基类和派生类的虚函数中
- 使用typeid或dynamic_cast检查运行时类型
- 打印虚函数表内容(编译器特定)
GDB示例:
code复制(gdb) p *obj
$1 = {_vptr.Base = 0x400d38 <vtable for Derived+16>}
(gdb) info vtbl obj
vtable for 'Derived' @ 0x400d30 (subobject @ 0x7fffffffe010):
[0]: 0x400b56 <Derived::foo()>
13.2 日志记录多态行为
在复杂多态系统中添加日志:
cpp复制class Loggable {
public:
virtual ~Loggable() = default;
virtual std::string getClassName() const = 0;
};
class MyClass : public Loggable {
std::string getClassName() const override {
return "MyClass";
}
};
void log(const Loggable& obj) {
std::cout << "Operating on: " << obj.getClassName() << std::endl;
}
这种技术可以帮助:
- 跟踪对象实际类型
- 理解多态调用流程
- 诊断与类型相关的问题
14. 多态在游戏开发中的应用实例
14.1 游戏实体组件系统
现代游戏引擎常用多态实现实体组件系统:
cpp复制class Component {
public:
virtual ~Component() = default;
virtual void update(float deltaTime) = 0;
virtual void render() const = 0;
};
class TransformComponent : public Component {
void update(float) override { /* 更新位置 */ }
void render() const override {} // 无渲染
};
class SpriteComponent : public Component {
void update(float) override {} // 无更新
void render() const override { /* 渲染精灵 */ }
};
class GameEntity {
std::vector<std::unique_ptr<Component>> components;
public:
void update(float deltaTime) {
for (auto& c : components) c->update(deltaTime);
}
void render() const {
for (auto& c : components) c->render();
}
template <typename T> T* addComponent() {
auto ptr = std::make_unique<T>();
auto raw = ptr.get();
components.push_back(std::move(ptr));
return raw;
}
};
14.2 状态模式实现角色行为
多态非常适合实现游戏角色的状态机:
cpp复制class CharacterState {
public:
virtual ~CharacterState() = default;
virtual void enter(Character&) = 0;
virtual void update(Character&, float deltaTime) = 0;
virtual void exit(Character&) = 0;
};
class IdleState : public CharacterState {
void enter(Character& c) override { c.setAnimation("idle"); }
void update(Character&, float) override {}
void exit(Character&) override {}
};
class RunningState : public CharacterState {
void enter(Character& c) override { c.setAnimation("run"); }
void update(Character& c, float dt) override {
c.move(c.getFacingDirection() * dt);
}
void exit(Character&) override {}
};
class Character {
std::unique_ptr<CharacterState> state;
public:
void changeState(std::unique_ptr<CharacterState> newState) {
if (state) state->exit(*this);
state = std::move(newState);
state->enter(*this);
}
void update(float dt) {
if (state) state->update(*this, dt);
}
};
15. 多态与并发编程
15.1 线程安全的多态对象
在多线程环境中使用多态对象需要注意:
cpp复制class ThreadSafeInterface {
std::mutex mtx;
public:
virtual ~ThreadSafeInterface() = default;
void safeOperation() {
std::lock_guard<std::mutex> lock(mtx);
doOperation();
}
protected:
virtual void doOperation() = 0;
};
class Implementation : public ThreadSafeInterface {
void doOperation() override {
// 实际实现,已被保护
}
};
关键点:
- 将锁放在接口层,确保所有实现都线程安全
- 避免在持有锁时调用虚函数(可能导致死锁)
- 考虑使用std::shared_mutex优化读多写少场景
15.2 异步回调中的多态
多态回调在异步编程中很常见:
cpp复制class Callback {
public:
virtual ~Callback() = default;
virtual void onSuccess(const std::string& result) = 0;
virtual void onError(int errorCode) = 0;
};
class AsyncOperation {
public:
void execute(Callback* cb) {
std::thread([this, cb] {
try {
auto result = doWork();
cb->onSuccess(result);
} catch (const std::exception& e) {
cb->onError(-1);
}
}).detach();
}
private:
std::string doWork() { /* 耗时操作 */ }
};
使用注意事项:
- 明确回调对象生命周期管理
- 考虑使用std::shared_ptr管理回调对象
- 确保线程安全的回调处理
16. 多态与序列化
16.1 多态对象的序列化挑战
序列化多态对象需要考虑类型信息:
cpp复制class Serializable {
public:
virtual ~Serializable() = default;
virtual std::string serialize() const = 0;
static std::unique_ptr<Serializable> deserialize(const std::string&);
};
class MyObject : public Serializable {
int id;
std::string name;
public:
std::string serialize() const override {
return std::to_string(id) + "," + name;
}
static std::unique_ptr<MyObject> create(const std::string& data) {
auto obj = std::make_unique<MyObject>();
size_t pos = data.find(',');
obj->id = std::stoi(data.substr(0, pos));
obj->name = data.substr(pos + 1);
return obj;
}
};
// 在Serializable中实现
std::unique_ptr<Serializable> Serializable::deserialize(const std::string& data) {
// 需要某种方式识别类型,例如前缀或注册表
if (data.starts_with("MyObject:")) {
return MyObject::create(data.substr(9));
}
return nullptr;
}
16.2 类型注册系统
更灵活的解决方案是类型注册系统:
cpp复制class SerializableRegistry {
using Creator = std::function<std::unique_ptr<Serializable>()>;
std::unordered_map<std::string, Creator> creators;
public:
template <typename T>
void registerType(const std::string& name) {
creators[name] = [] { return std::make_unique<T>(); };
}
std::unique_ptr<Serializable> create(const std::string& name) {
if (auto it = creators.find(name); it != creators.end()) {
return it->second();
}
return nullptr;
}
};
17. 多态与内存管理
17.1 自定义内存分配策略
多态对象可以结合自定义内存分配:
cpp复制class Allocator {
public:
virtual ~Allocator() = default;
virtual void* allocate(size_t) = 0;
virtual void deallocate(void*) = 0;
};
class PoolAllocator : public Allocator {
void* allocate(size_t) override { /* 实现 */ }
void deallocate(void*) override { /* 实现 */ }
};
class PolymorphicObject {
static Allocator* allocator;
public:
static void setAllocator(Allocator* a) { allocator = a; }
void* operator new(size_t size) {
return allocator ? allocator->allocate(size) : ::operator new(size);
}
void operator delete(void* p) {
if (allocator) allocator->deallocate(p);
else ::operator delete(p);
}
};
17.2 对象池模式
多态对象池可以提高性能:
cpp复制template <typename Base>
class ObjectPool {
std::vector<std::unique_ptr<Base>> pool;
public:
template <typename Derived, typename... Args>
Derived* create(Args&&... args) {
static_assert(std::is_base_of_v<Base, Derived>);
auto obj = std::make_unique<Derived>(std::forward<Args>(args)...);
auto raw = obj.get();
pool.push_back(std::move(obj));
return raw;
}
void clear() { pool.clear(); }
};
使用场景:
- 频繁创建销毁的多态对象
- 需要严格控制内存分配的实时系统
- 对象初始化成本高的场景
18. 多态与反射
18.1 简易运行时类型信息
可以扩展多态基类提供反射能力:
cpp复制class ReflectiveObject {
public:
virtual ~ReflectiveObject() = default;
virtual std::string getClassName() const = 0;
virtual std::vector<std::string> getPropertyNames() const = 0;
virtual void setProperty(const std::string& name, const std::string& value) = 0;
virtual std::string getProperty(const std::string& name) const = 0;
};
class Person : public ReflectiveObject {
std::string name;
int age;
public:
std::string getClassName() const override { return "Person"; }
std::vector<std::string> getPropertyNames() const override {
return {"name", "age"};
}
void setProperty(const std::string& name, const std::string& value) override {
if (name == "name") this->name = value;
else if (name == "age") age = std::stoi(value);
}
std::string getProperty(const std::string& name) const override {
if (name == "name") return name;
if (name == "age") return std::to_string(age);
return "";
}
};
18.2 序列化与反序列化框架
结合多态和反射可以实现通用序列化:
cpp复制class Serializable {
public:
virtual std::string toJson() const = 0;
virtual void fromJson(const std::string&) = 0;
};
class JsonSerializer {
public:
template <typename T>
static std::string serialize(const T& obj) {
if constexpr (std::is_base_of_v<Serializable, T>) {
return obj.toJson();
} else {
// 使用反射或其他方式处理普通类型
}
}
};
19. 多态与跨语言交互
19.1 C++多态接口的C封装
将C++多态接口暴露给C代码:
cpp复制// C++头文件
class PluginInterface {
public:
virtual ~PluginInterface() = default;
virtual void doWork() = 0;
};
extern "C" {
typedef void* PluginHandle;
PluginHandle createPlugin();
void callPluginDoWork(PluginHandle);
void destroyPlugin(PluginHandle);
}
// C++实现
PluginHandle createPlugin() {
return reinterpret_cast<PluginHandle>(new MyPlugin());
}
void callPluginDoWork(PluginHandle h) {
auto plugin = reinterpret_cast<PluginInterface*>(h);
plugin->doWork();
}
void destroyPlugin(PluginHandle h) {
delete reinterpret_cast<PluginInterface*>(h);
}
19.2 与脚本语言集成
将多态对象暴露给Python等脚本语言:
cpp复制// 使用pybind11示例
class Animal {
public:
virtual ~Animal() = default;
virtual std::string go(int n_times) = 0;
};
class Dog : public Animal {
std::string go(int n_times) override {
std::string result;
for (int i = 0; i < n_times; ++i)
result += "woof! ";
return result;
}
};
PYBIND11_MODULE(example, m) {
py::class_<Animal>(m, "Animal")
.def("go", &Animal::go);
py::class_<Dog, Animal>(m, "Dog")
.def(py::init<>());
m.def("get_animal", []() -> std::unique_ptr<Animal> {
return std::make_unique<Dog>();
});
}
20. 多态设计的高级主题
20.1 类型擦除技术
类型擦除是一种强大的多态技术:
cpp复制class AnyCallable {
struct Concept {
virtual ~Concept() = default;
virtual void operator()() = 0;
};
template <typename T>
struct Model : Concept {
T callable;
Model(T c) : callable(std::move(c)) {}
void operator()() override { callable(); }
};
std::unique_ptr<Concept> concept;
public:
template <typename T>
AnyCallable(T&& callable)
: concept(std::make_unique<Model<std::decay_t<T>>>(std::forward<T>(callable))) {}
void operator()() { (*concept)(); }
};
// 使用示例
AnyCallable func1 = [] { std::cout << "Lambda\n"; };
AnyCallable func2 = std::bind(&SomeClass::method, &obj);
func1();
func2();
20.2 多态值语义
使用多态对象作为值类型:
cpp复制class PolymorphicValue {
struct Concept {
virtual ~Concept() = default;
virtual std::unique_ptr<Concept> clone() const = 0;
virtual void draw() const = 0;
};
template <typename T>
struct Model : Concept {
T value;
Model(T v) : value(std::move(v)) {}
std::unique_ptr<Concept> clone() const override {
return std::make_unique<Model>(value);
}
void draw() const override { value.draw(); }
};
std::unique_ptr<Concept> ptr;
public:
template <typename T>
PolymorphicValue(T value) : ptr(std::make_unique<Model<T>>(std::move(value))) {}
PolymorphicValue(const PolymorphicValue& other)
: ptr(other.ptr ? other.ptr->clone() : nullptr) {}
void draw() const { if (ptr) ptr->draw(); }
};
这种技术结合了多态的灵活性和值语义的便利性,适用于需要多态但又希望避免指针语义的场景。