【C++重载操作符与转换】句柄类与继承

目录

一、句柄类的基本概念

1.1 什么是句柄类

1.2 句柄类的设计动机

1.3 句柄类的基本结构

二、句柄类的实现方式

2.1 基于指针的句柄类

2.2 值语义的句柄类

2.3 引用计数的句柄类

三、句柄类与继承的结合应用

3.1 实现多态容器

3.2 实现插件系统

3.3 实现状态模式

四、句柄类的优缺点

4.1 优点

4.2 缺点

五、句柄类与智能指针的比较

5.1 相似之处

5.2 不同之处

六、句柄类的设计考虑

6.1 复制控制

6.2 异常安全

6.3 虚函数表

6.4 接口设计

七、完整代码示例

7.1 图形库示例 

7.2 文档格式转换器示例 

八、总结


在 C++ 面向对象编程中,句柄类 (Handle Class) 是一种强大的设计模式,它允许我们以统一的接口操作不同类型的对象,同时隐藏对象的具体实现细节。结合继承和多态机制,句柄类可以实现灵活的对象管理,尤其适用于需要处理多种派生类对象的场景。

一、句柄类的基本概念

1.1 什么是句柄类

句柄类是一种包装类,它封装了对另一个对象 (通常是基类指针) 的访问。句柄类的主要作用是提供一个统一的接口,隐藏底层对象的具体类型和实现细节,同时允许通过多态机制操作不同的派生类对象。

1.2 句柄类的设计动机

  • 解耦接口与实现:客户端只需通过句柄类提供的接口操作对象,无需关心对象的具体类型和实现。
  • 管理对象生命周期:句柄类可以负责对象的创建、复制和销毁,简化资源管理。
  • 实现多态:句柄类内部通过基类指针或引用实现多态,允许在运行时动态绑定到不同的派生类对象。

1.3 句柄类的基本结构

一个典型的句柄类包含以下部分:

  • 一个指向基类的指针或引用
  • 构造函数,用于初始化底层对象
  • 复制控制函数 (拷贝构造函数、赋值运算符、析构函数)
  • 转发调用到底层对象的成员函数
class Handle {
private:Base* ptr;  // 指向基类的指针
public:// 构造函数Handle(Base* p) : ptr(p) {}// 析构函数~Handle() { delete ptr; }// 拷贝构造函数Handle(const Handle& other);// 赋值运算符Handle& operator=(const Handle& other);// 转发调用到底层对象void callMethod() { ptr->method(); }
};

二、句柄类的实现方式

2.1 基于指针的句柄类

最常见的句柄类实现方式是通过指针管理底层对象。这种方式允许句柄类在运行时动态绑定到不同的派生类对象。 

class Base {
public:virtual void print() const = 0;virtual ~Base() {}
};class Derived1 : public Base {
public:void print() const override { std::cout << "Derived1" << std::endl; }
};class Derived2 : public Base {
public:void print() const override { std::cout << "Derived2" << std::endl; }
};class Handle {
private:Base* ptr;
public:// 构造函数Handle(Base* p) : ptr(p) {}// 析构函数~Handle() { delete ptr; }// 拷贝构造函数 - 深拷贝Handle(const Handle& other) {if (other.ptr) {ptr = other.ptr->clone();  // 假设Base定义了纯虚函数clone()} else {ptr = nullptr;}}// 赋值运算符Handle& operator=(const Handle& other) {if (this != &other) {delete ptr;if (other.ptr) {ptr = other.ptr->clone();} else {ptr = nullptr;}}return *this;}// 转发调用void print() const {if (ptr) ptr->print();}
};

2.2 值语义的句柄类

值语义的句柄类在复制时会创建底层对象的副本,而不是简单地复制指针。这种方式提供了更直观的对象行为,但需要确保底层对象支持复制操作。 

class ValueHandle {
private:std::unique_ptr<Base> ptr;  // 使用智能指针管理内存
public:// 构造函数template<typename T>ValueHandle(T obj) : ptr(std::make_unique<T>(std::move(obj))) {}// 拷贝构造函数 - 深拷贝ValueHandle(const ValueHandle& other) {if (other.ptr) {ptr = other.ptr->clone();  // 调用虚函数clone()创建副本}}// 移动构造函数ValueHandle(ValueHandle&& other) noexcept = default;// 赋值运算符ValueHandle& operator=(ValueHandle other) {swap(*this, other);return *this;}// 交换函数friend void swap(ValueHandle& a, ValueHandle& b) noexcept {using std::swap;swap(a.ptr, b.ptr);}// 转发调用void print() const {if (ptr) ptr->print();}
};

2.3 引用计数的句柄类

引用计数的句柄类通过维护一个引用计数来管理底层对象的生命周期,当最后一个引用被销毁时才释放对象。 

class RefCountHandle {
private:Base* ptr;int* count;  // 引用计数void acquire() {if (ptr) ++(*count);}void release() {if (ptr) {if (--(*count) == 0) {delete ptr;delete count;}}}
public:// 构造函数RefCountHandle(Base* p = nullptr) : ptr(p), count(new int(1)) {}// 拷贝构造函数RefCountHandle(const RefCountHandle& other) : ptr(other.ptr), count(other.count) {acquire();}// 赋值运算符RefCountHandle& operator=(const RefCountHandle& other) {if (this != &other) {release();ptr = other.ptr;count = other.count;acquire();}return *this;}// 析构函数~RefCountHandle() {release();}// 转发调用void print() const {if (ptr) ptr->print();}
};

三、句柄类与继承的结合应用

3.1 实现多态容器

句柄类可以用于实现多态容器,允许在同一个容器中存储不同类型的对象。 

#include <iostream> 
#include <vector>
#include <memory>// 手动实现make_unique
#if __cplusplus < 201402L
namespace std {template<typename T, typename... Args>unique_ptr<T> make_unique(Args&&... args) {return unique_ptr<T>(new T(std::forward<Args>(args)...));}
}
#endifclass Shape {
public:virtual double area() const = 0;virtual ~Shape() {}
};class Circle : public Shape {
private:double radius;
public:Circle(double r) : radius(r) {}double area() const override { return 3.14 * radius * 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; }
};// 句柄类
class ShapeHandle {
private:std::unique_ptr<Shape> ptr;
public:template<typename T>ShapeHandle(T obj) : ptr(std::make_unique<T>(std::move(obj))) {}double area() const { return ptr->area(); }
};// 使用句柄类的多态容器
int main() {std::vector<ShapeHandle> shapes;shapes.emplace_back(Circle(5.0));shapes.emplace_back(Rectangle(3.0, 4.0));for (const auto& shape : shapes) {std::cout << "Area: " << shape.area() << std::endl;}return 0;
}

3.2 实现插件系统

句柄类可以用于实现插件系统,允许程序在运行时动态加载和使用不同的插件。 

// 插件接口
class Plugin {
public:virtual void execute() = 0;virtual ~Plugin() {}
};// 插件管理器
class PluginManager {
private:std::vector<std::unique_ptr<Plugin>> plugins;
public:// 加载插件template<typename T>void loadPlugin() {plugins.push_back(std::make_unique<T>());}// 执行所有插件void executeAll() {for (const auto& plugin : plugins) {plugin->execute();}}
};

3.3 实现状态模式

句柄类可以用于实现状态模式,允许对象在不同状态之间切换,而不需要修改对象的接口。 

// 状态接口
class State {
public:virtual void handle() = 0;virtual ~State() {}
};// 具体状态
class ConcreteStateA : public State {
public:void handle() override { std::cout << "Handling state A" << std::endl; }
};class ConcreteStateB : public State {
public:void handle() override { std::cout << "Handling state B" << std::endl; }
};// 上下文类
class Context {
private:std::unique_ptr<State> state;
public:Context() : state(std::make_unique<ConcreteStateA>()) {}void setState(std::unique_ptr<State> newState) {state = std::move(newState);}void request() {state->handle();}
};

四、句柄类的优缺点

4.1 优点

  • 实现多态:句柄类通过基类指针或引用实现多态,允许统一处理不同类型的对象。
  • 解耦接口与实现:客户端只需要与句柄类交互,不需要了解底层对象的具体类型和实现。
  • 简化资源管理:句柄类可以负责对象的生命周期管理,减少内存泄漏的风险。
  • 提供值语义:通过适当的复制控制,句柄类可以提供值语义,使对象的行为更加直观。

4.2 缺点

  • 性能开销:虚函数调用和动态内存分配会带来一定的性能开销。
  • 实现复杂度:句柄类的实现需要处理复制控制、内存管理等复杂问题,容易引入错误。
  • 可能的内存碎片化:频繁的动态内存分配和释放可能导致内存碎片化。

五、句柄类与智能指针的比较

5.1 相似之处

  • 都用于管理动态分配的对象
  • 都提供了自动内存管理功能
  • 都可以通过基类指针实现多态

5.2 不同之处

  • 句柄类
    • 提供更高级的抽象,隐藏底层对象的具体类型
    • 可以自定义对象的复制和销毁行为
    • 通常提供值语义
  • 智能指针
    • 主要关注内存管理
    • 提供标准的所有权语义(如 unique_ptr、shared_ptr)
    • 不隐藏底层对象的类型

六、句柄类的设计考虑

6.1 复制控制

句柄类必须正确处理复制控制(拷贝构造函数、赋值运算符和析构函数),以确保对象的生命周期得到正确管理。根据需求,可以实现深拷贝、引用计数或禁止复制。

6.2 异常安全

句柄类的操作应该是异常安全的,特别是在涉及动态内存分配和资源管理时。

6.3 虚函数表

使用句柄类时,需要确保基类定义了适当的虚函数,以便实现多态调用。

6.4 接口设计

句柄类的接口应该简洁明了,只暴露必要的操作,隐藏底层实现细节。

七、完整代码示例

7.1 图形库示例 

#include <iostream>
#include <memory>
#include <vector>// 手动实现 make_unique
#if __cplusplus < 201402L
namespace std {template<typename T, typename... Args>std::unique_ptr<T> make_unique(Args&&... args) {return std::unique_ptr<T>(new T(std::forward<Args>(args)...));}
}
#endif// 基类
class Shape {
public:virtual double area() const = 0;virtual std::string name() const = 0;virtual std::unique_ptr<Shape> clone() const = 0;virtual ~Shape() {}
};// 派生类
class Circle : public Shape {
private:double radius;
public:Circle(double r) : radius(r) {}double area() const override { return 3.14 * radius * radius; }std::string name() const override { return "Circle"; }std::unique_ptr<Shape> clone() const override {return std::make_unique<Circle>(*this);}
};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; }std::string name() const override { return "Rectangle"; }std::unique_ptr<Shape> clone() const override {return std::make_unique<Rectangle>(*this);}
};// 句柄类
class ShapeHandle {
private:std::unique_ptr<Shape> ptr;
public:// 构造函数template<typename T>ShapeHandle(T obj) : ptr(std::make_unique<T>(std::move(obj))) {}// 拷贝构造函数ShapeHandle(const ShapeHandle& other) : ptr(other.ptr->clone()) {}// 移动构造函数ShapeHandle(ShapeHandle&& other) noexcept = default;// 赋值运算符ShapeHandle& operator=(ShapeHandle other) {swap(*this, other);return *this;}// 交换函数friend void swap(ShapeHandle& a, ShapeHandle& b) noexcept {using std::swap;swap(a.ptr, b.ptr);}// 转发调用double area() const { return ptr->area(); }std::string name() const { return ptr->name(); }
};// 使用示例
int main() {// 创建句柄对象ShapeHandle circle(Circle(5.0));ShapeHandle rectangle(Rectangle(3.0, 4.0));// 使用句柄对象std::cout << circle.name() << " area: " << circle.area() << std::endl;std::cout << rectangle.name() << " area: " << rectangle.area() << std::endl;// 创建多态容器std::vector<ShapeHandle> shapes;shapes.push_back(circle);shapes.push_back(rectangle);// 遍历容器for (const auto& shape : shapes) {std::cout << shape.name() << " area: " << shape.area() << std::endl;}return 0;
}

7.2 文档格式转换器示例 

#include <iostream>
#include <memory>
#include <string>// 手动实现 make_unique (C++11 适用)
#if __cplusplus < 201402L
namespace std {template<typename T, typename... Args>std::unique_ptr<T> make_unique(Args&&... args) {return std::unique_ptr<T>(new T(std::forward<Args>(args)...));}
}
#endif// 文档接口
class Document {
public:virtual void convertToPDF() = 0;virtual void convertToWord() = 0;virtual ~Document() {}
};// Word文档
class WordDocument : public Document {
public:void convertToPDF() override {std::cout << "Converting Word document to PDF..." << std::endl;}void convertToWord() override {std::cout << "Word document is already in Word format." << std::endl;}
};// PDF文档
class PDFDocument : public Document {
public:void convertToPDF() override {std::cout << "PDF document is already in PDF format." << std::endl;}void convertToWord() override {std::cout << "Converting PDF document to Word..." << std::endl;}
};// 文档句柄类
class DocumentHandle {
private:std::unique_ptr<Document> ptr;
public:template<typename T>DocumentHandle(T obj) : ptr(std::make_unique<T>(std::move(obj))) {}void convertToPDF() { ptr->convertToPDF(); }void convertToWord() { ptr->convertToWord(); }
};// 使用示例
int main() {DocumentHandle wordDoc{WordDocument()};DocumentHandle pdfDoc{PDFDocument()};wordDoc.convertToPDF();wordDoc.convertToWord();pdfDoc.convertToPDF();pdfDoc.convertToWord();return 0;
}

八、总结

句柄类是 C++ 中一种强大的设计模式,它结合了继承和多态机制,提供了一种灵活且安全的方式来管理不同类型的对象。通过封装底层对象的实现细节,句柄类可以实现接口与实现的解耦,简化资源管理,并提供统一的操作接口。

在设计和实现句柄类时,需要注意以下几点:

  1. 正确处理复制控制,根据需求选择深拷贝、引用计数或禁止复制
  2. 使用智能指针管理动态内存,避免内存泄漏
  3. 确保基类定义了适当的虚函数,实现多态调用
  4. 设计简洁明了的接口,隐藏底层实现细节
  5. 考虑异常安全性,确保操作在异常情况下也能正确释放资源

通过合理运用句柄类,可以构建更加灵活、可维护的 C++ 程序,充分发挥面向对象编程的优势。 


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/80222.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

谷歌曾经的开放重定向漏洞(如今已经修复) -- noogle DefCamp 2024

题目描述: 上周&#xff0c;我决定创建自己的搜索引擎。这有点难&#xff0c;所以我背上了另一个。我也在8000端口上尝试了一些东西。 未发现题目任何交互,但是存在一个加密js const _0x43a57f _0x22f9; (function(_0x3d7d57, _0x426e05) {const _0x16c3fa _0x22f9, _0x3187…

【C#】ToArray的使用

在 C# 中&#xff0c;ToArray 方法通常用于将实现了 IEnumerable<T> 接口的集合&#xff08;如 List<T>&#xff09;转换为数组。这个方法是 LINQ 提供的一个扩展方法&#xff0c;位于 System.Linq 命名空间中。因此&#xff0c;在使用 ToArray 方法之前&#xff0…

资产管理平台—chemex

1、简介 Chemex CMDB&#xff08;Configuration Management Database&#xff09;是一个基于现代微服务架构的资产管理与自动化平台&#xff0c;专为 IT 基础设施与业务资产管理而设计。其核心目标是解决大规模系统运维中资产信息混乱、配置分散、数据不一致等问题&#xff0c…

【AI】mcp server是什么玩意儿

文章目录 背景mcp server的必要性mcp server的基本概念mcp server的架构与核心组件总结 背景 劈里啪啦的整了一堆概念&#xff0c;对mcp server还是只停留在知道个词的地步。 虽然目前大模型的对话生成能力很强&#xff0c;但是大模型&#xff08;如deepseek&#xff09;并不能…

c# 数据结构 树篇 入门树与二叉树的一切

事先声明,本文不适合对数据结构完全不懂的小白 请至少学会链表再阅读 c# 数据结构 链表篇 有关单链表的一切_c# 链表-CSDN博客 数据结构理论先导:《数据结构&#xff08;C 语言描述&#xff09;》也许是全站最良心最通俗易懂最好看的数据结构课&#xff08;最迟每周五更新~~&am…

《Cookie Cutter》中2000多张精灵表与10000个2D光源的管理之道

一个小团队如何在多个平台上以优秀的效果展示手绘动画&#xff1f;Subcult Joint 工作室给出了答案。他们用六年时间开发出了游戏《Cookie Cutter》。游戏中使用了数千个使用传统动画技术制作的高分辨率资产&#xff0c;而且这些资产都在 Unity 中进行了优化。由于工作室需要在…

什么是实景VR?实景VR应用场景

实景VR&#xff0c;即基于真实场景的虚拟现实技术&#xff0c;是利用计算机技术生成三维环境&#xff0c;以模拟并再现真实世界场景的技术。 用户通过佩戴VR设备&#xff08;如VR头盔、手柄等&#xff09;或通过电脑设备&#xff0c;可以沉浸在一个高度仿真的虚拟环境中&#…

内核性能测试(60s不丢包性能)

以xGAP-200-SE7K-L&#xff08;双口10G&#xff09;在飞腾D2000上为例&#xff08;单通道最高性能约2.8Gbps) 单口测试 0口&#xff1a; tcp&#xff1a; taskset -c 4 iperf -c 1.1.1.1 -i 1 -t 60 -p 60001 taskset -c 4 iperf -s -i 1 -p 60001 udp&#xff1a; taskse…

58. 区间和

题目链接&#xff1a; 58. 区间和 题目描述&#xff1a; 给定一个整数数组 Array&#xff0c;请计算该数组在每个指定区间内元素的总和。 输入描述 第一行输入为整数数组 Array 的长度 n&#xff0c;接下来 n 行&#xff0c;每行一个整数&#xff0c;表示数组的元素。随后…

C#进阶(2)stack(栈)

前言 我们前面介绍了ArrayList,今天就介绍另一种数据结构——栈。 这是栈的基本形式,博主简单画了一下,你看个意思就行,很明显,这种数据有一种特征:先进后出。因为先进来的数据会在下面,下面是密闭的,所以只能取后面进来的。 C#为我们封好了这种数据结构,我们不用担…

汽车工厂数字孪生实时监控技术从数据采集到三维驱动实现

在工业智能制造推动下&#xff0c;数字孪生技术正成为制造业数字化转型的核心驱动力。今天详细介绍数字孪生实时监控技术在汽车工厂中的应用&#xff0c;重点解析从数据采集到三维驱动实现的全流程技术架构&#xff0c;并展示其在提升生产效率、降低成本和优化决策方面的显著价…

git|gitee仓库同步到github

参考&#xff1a;一次提交更新两个仓库&#xff0c;Get 更优雅的 GitHub/Gitee 仓库镜像同步 文章目录 进入需要使用镜像功能的仓库&#xff0c;进入「管理」找到「仓库镜像管理」选项&#xff0c;点击「添加镜像」按钮绑定github绑定成功后再次点击添加镜像如何申请 GitHub 私…

原生小程序+springboot+vue+协同过滤算法的音乐推荐系统(源码+论文+讲解+安装+部署+调试)

感兴趣的可以先收藏起来&#xff0c;还有大家在毕设选题&#xff0c;项目以及论文编写等相关问题都可以给我留言咨询&#xff0c;我会一一回复&#xff0c;希望帮助更多的人。 系统背景 在数字音乐产业迅猛发展的当下&#xff0c;Spotify、QQ 音乐、网易云音乐等音乐平台的曲…

RustDesk

配置中继服务器 https://rustdesk.com/docs/zh-cn/self-host/windows/ 服务器端 下载Windows版本 rustdesk-server-windows-x86_64.zip&#xff0c;安装路径为&#xff1a;C:\Program Files\RustDeskServer\bin。执行 hbbr.exe 和 hbbs.exe 两个应用程序。这两个应用提供了两…

django中用 InforSuite RDS 替代memcache

在 Django 项目中&#xff0c;InforSuite RDS&#xff08;关系型数据库服务&#xff09;无法直接替代 Memcached&#xff0c;因为两者的设计目标和功能定位完全不同&#xff1a; 特性MemcachedInforSuite RDS核心用途高性能内存缓存&#xff0c;临时存储键值对数据持久化关系型…

leetcode 57. Insert Interval

题目描述 代码&#xff1a;由于intervals已经按照左端点排序&#xff0c;并且intervals中的区间全部不重叠&#xff0c;那么可以断定intervals中所有区间的右端点也已经是有序的。先二分查找intervals中第一个其右端点>newInterval左端点的区间。然后按照类似于56. Merge In…

去年开发一款鸿蒙Next Os的window工具箱

持拖载多个鸿蒙应用 批量签名安装 运行 http://dl.lozn.top/lozn/HarmonySignAndFileManagerTool_2024-11-26.zip 同类型安卓工具箱以及其他软件下载地址汇总 http://dl.lozn.top/lozn/ 怎么个玩法呢&#xff0c;比如要启动某app, 拖载识别到包名 点启动他能主动读取包名 然后…

Trivy:让你时刻掌控的开源安全扫描器

深入了解 Trivy:全面的安全扫描工具 在如今互联网快速发展的时代,软件的安全性显得尤为重要。随着应用程序的复杂性增加,其可能带来的安全漏洞也在不断增多。如何快速、准确地发现这些潜在威胁是每个开发者和运维人员心中的课题。今天,我们将为大家介绍一个开源的安全扫描…

网址为 http://xxx:xxxx/的网页可能暂时无法连接,或者它已永久性地移动到了新网址

这是由于浏览器默认的非安全端口所导致的&#xff0c;所谓非安全端口&#xff0c;就是浏览器出于安全问题&#xff0c;会禁止一些网络浏览向外的端口。 避免使用6000,6666这样的端口 6000-7000有很多都不行&#xff0c;所以尽量避免使用这个区间 还有在云服务器中&#xff0c…

Jenkins 执行器(Executor)如何调整限制?

目录 现象原因解决 现象 Jenkins 构建时&#xff0c;提示如下&#xff1a; 此刻的心情正如上图中的小老头&#xff0c;火冒三丈&#xff0c;但是不要急&#xff0c;因为每一次错误&#xff0c;都是系统中某个环节在说‘我撑不住了’。 原因 其实是上图的提示表示 Jenkins 当…