讲师中心 微信公众号
AI工具推荐 视频效率加速

如何设计C++中的代理模式 虚拟代理与保护代理应用场景

千磊酱_7603

千磊酱_7603

发布时间:2025-08-03 09:51:01

|

339人浏览过

|

来源于php中文网

原创

设计c++中的代理模式需定义共同接口、实现真实主题和实现代理。1. 定义共同接口(isubject),确保代理和真实对象具有一致的访问入口;2. 实现真实主题(realsubject),执行核心业务逻辑;3. 实现代理(proxy),持有真实对象指针并控制其访问,可在调用前后插入额外逻辑。虚拟代理通过延迟加载优化性能,如图片加载器、远程服务代理和数据库连接场景。保护代理用于权限控制,如文档编辑权限、系统配置管理和api访问控制,通过验证用户角色决定是否转发请求。使用时需权衡复杂性、性能开销、调试难度、接口同步、生命周期管理等问题,避免过度设计。

如何设计C++中的代理模式 虚拟代理与保护代理应用场景

在C++中设计代理模式,本质上是引入一个“替身”或“守门员”对象,来控制对另一个对象的访问。这个模式的核心在于提供一层间接性,让我们可以在访问实际对象之前或之后,插入额外的逻辑,例如延迟加载资源(虚拟代理)或者进行权限验证(保护代理)。它不是简单地转发调用,而是赋予了我们对访问过程更精细的控制力。

如何设计C++中的代理模式 虚拟代理与保护代理应用场景

如何设计C++中的代理模式

代理模式的核心思想是为某个对象提供一个替代品或占位符,由代理对象来控制对真实对象的访问。在C++中实现这一点,通常会遵循以下结构:

如何设计C++中的代理模式 虚拟代理与保护代理应用场景
  1. 定义共同接口 (Subject Interface):这是最关键的一步。真实对象(RealSubject)和代理对象(Proxy)都需要实现同一个接口(抽象基类)。这样,客户端代码就可以通过这个接口来与代理或真实对象交互,而无需知道背后是哪个具体实现。

    立即学习“C++免费学习笔记(深入)”;

    // ISubject.h
    class ISubject {
    public:
        virtual void request() = 0;
        virtual ~ISubject() = default;
    };
  2. 实现真实主题 (RealSubject):这是实际执行业务逻辑的对象。它实现了

    ISubject
    接口中定义的方法。

    如何设计C++中的代理模式 虚拟代理与保护代理应用场景
    // RealSubject.h
    #include <iostream>
    class RealSubject : public ISubject {
    public:
        void request() override {
            std::cout << "RealSubject: Handling request." << std::endl;
        }
    };
  3. 实现代理 (Proxy):代理对象也实现

    ISubject
    接口。它内部会持有一个指向
    RealSubject
    对象的指针(通常是智能指针,如
    std::unique_ptr
    或
    std::shared_ptr
    ,以管理生命周期)。代理在调用
    RealSubject
    的方法之前或之后,可以执行自己的逻辑。

    // Proxy.h
    #include "ISubject.h"
    #include "RealSubject.h"
    #include <memory> // For std::unique_ptr
    
    class Proxy : public ISubject {
    private:
        std::unique_ptr<RealSubject> realSubject; // 代理持有真实对象的引用
    
    public:
        // 构造函数可以按需初始化或延迟初始化
        Proxy() : realSubject(nullptr) {}
    
        void request() override {
            // 在调用真实对象方法前执行一些逻辑
            std::cout << "Proxy: Pre-processing before real subject request." << std::endl;
    
            // 确保真实对象存在,如果不存在则创建(虚拟代理的体现)
            if (!realSubject) {
                realSubject = std::make_unique<RealSubject>();
                std::cout << "Proxy: RealSubject created on demand." << std::endl;
            }
    
            // 委托给真实对象执行核心逻辑
            realSubject->request();
    
            // 在调用真实对象方法后执行一些逻辑
            std::cout << "Proxy: Post-processing after real subject request." << std::endl;
        }
    };

    客户端代码会通过

    ISubject
    指针来操作,它可能持有
    RealSubject
    的实例,也可能持有
    Proxy
    的实例,但对于客户端而言,行为是透明的。这种设计模式让代码更具弹性,能够轻松地在不修改核心业务逻辑的情况下,增加额外的功能层。

虚拟代理在C++中的实现与典型场景

虚拟代理,顾名思义,就是提供一个“假象”,让客户端以为它正在直接操作一个对象,但实际上,这个对象的创建和初始化被推迟了,直到真正需要它的时候。这在C++中尤其有用,因为我们经常需要管理资源和性能。

它的实现要点在于,代理对象在构造时并不会立即创建真实的、可能资源密集型的对象。相反,它只持有一个指向真实对象的指针,或者一个用于创建真实对象的工厂方法。只有当客户端代码第一次调用真实对象的方法时,代理才会去创建并初始化那个真实的、重量级的对象,然后将后续的请求都转发给它。

典型场景:

  • 图片加载器: 设想一个需要显示大量高分辨率图片的应用程序。如果一次性加载所有图片到内存,程序可能会崩溃或启动速度极慢。使用虚拟代理,我们可以在界面上先显示一个占位符或缩略图,只有当用户滚动到某个图片并真正需要查看它时,代理才去加载这张大图。

    // 假设有一个 Image 接口和 RealImage 类
    // class IImage { public: virtual void display() = 0; };
    // class RealImage : public IImage { /* ... */ };
    
    class LazyImageProxy : public IImage {
    private:
        std::string filename;
        std::unique_ptr<RealImage> realImage; // 延迟创建的真实图片对象
    
    public:
        LazyImageProxy(const std::string& file) : filename(file), realImage(nullptr) {}
    
        void display() override {
            if (!realImage) {
                std::cout << "LazyImageProxy: Loading image from " << filename << "..." << std::endl;
                realImage = std::make_unique<RealImage>(filename); // 第一次调用时才加载
            }
            realImage->display();
        }
    };

    这里,

    LazyImageProxy
    构造时只保存了文件名,
    RealImage
    对象的实际加载(可能涉及文件I/O、内存分配)被推迟到了
    display()
    方法第一次被调用时。

  • 远程服务代理: 当客户端需要与一个远程服务(例如通过网络RPC)交互时,虚拟代理可以用来表示这个远程对象。只有当客户端真正发起一个远程调用时,代理才建立网络连接,并进行数据传输。这避免了不必要的连接开销。

  • 数据库连接: 一个应用程序可能在启动时并不需要立即连接数据库,但当用户执行某个操作需要数据时才建立连接。虚拟代理可以封装这个连接过程,确保连接只在必要时才被建立。

在我看来,虚拟代理就像是“按需服务”。它避免了不必要的资源消耗,特别是在启动阶段或者资源有限的环境下。它的价值在于性能优化和资源管理,让系统显得更“轻量”和响应更快。

Accessibility Review
Accessibility Review

对设计或页面进行 WCAG 2.1 AA 无障碍审计。触发关键词:“audit accessibility”、 “check a11y”、 “is this accessible?” 或在审查设计时使用。

下载

保护代理的C++应用实践与权限控制

保护代理,顾名思义,它的主要职责是控制对真实对象的访问权限。它充当了一个“守卫”的角色,在客户端尝试访问真实对象之前,会进行一系列的检查,比如用户身份验证、权限级别、操作合法性等。如果检查通过,它才将请求转发给真实对象;否则,它会拒绝访问,或者返回一个错误。

实现要点: 保护代理会包含一个逻辑,用于判断当前操作者是否具有执行特定操作的权限。这通常涉及到:

  1. 获取操作者身份: 可能是通过用户会话、令牌等方式。
  2. 定义权限规则: 哪些用户角色可以执行哪些操作。
  3. 执行权限检查: 在代理的方法中,根据操作者身份和操作类型,判断是否允许访问。

典型应用实践:

  • 文档编辑权限: 考虑一个多人协作的文档系统。有些用户只能阅读文档,有些可以编辑,而只有管理员才能删除。保护代理可以在访问文档对象的方法(如

    edit()
    、
    delete()
    )时,检查当前用户的角色。

    // 假设有 IUser 和 IDocument 接口
    // class IUser { public: enum Role { Guest, Editor, Admin }; Role getRole() const; };
    // class IDocument { public: virtual void read() = 0; virtual void edit() = 0; virtual void remove() = 0; };
    // class RealDocument : public IDocument { /* ... */ };
    
    class ProtectedDocumentProxy : public IDocument {
    private:
        std::unique_ptr<RealDocument> realDocument;
        const IUser& currentUser; // 当前操作用户
    
    public:
        ProtectedDocumentProxy(std::unique_ptr<RealDocument> doc, const IUser& user)
            : realDocument(std::move(doc)), currentUser(user) {}
    
        void read() override {
            // 任何人都可以读
            realDocument->read();
        }
    
        void edit() override {
            if (currentUser.getRole() == IUser::Editor || currentUser.getRole() == IUser::Admin) {
                realDocument->edit();
            } else {
                std::cout << "ProtectedDocumentProxy: Access Denied! Insufficient permissions to edit." << std::endl;
            }
        }
    
        void remove() override {
            if (currentUser.getRole() == IUser::Admin) {
                realDocument->remove();
            } else {
                std::cout << "ProtectedDocumentProxy: Access Denied! Only administrators can remove documents." << std::endl;
            }
        }
    };

    这个例子中,

    ProtectedDocumentProxy
    根据
    currentUser
    的角色,动态决定是否允许调用
    RealDocument
    的
    edit()
    或
    remove()
    方法。

  • 系统配置管理: 在一个复杂的系统中,某些配置项可能非常敏感,只有特定的管理员用户才能修改。保护代理可以确保只有拥有相应权限的用户才能调用修改配置的方法。

  • API访问控制: 在微服务架构中,一个服务可能需要暴露API给不同的客户端。通过保护代理,可以根据客户端的API密钥或令牌,限制它们可以访问哪些API端点或执行哪些操作。

保护代理的价值在于它将安全逻辑与核心业务逻辑分离。真实对象只关心它自己的业务,而代理则专注于权限验证。这使得系统更安全,也更容易维护,因为权限规则的修改不会影响到核心业务代码。它就像一个智能的门禁系统,确保只有被授权的人才能进入或操作特定的区域。

代理模式设计中的常见陷阱与权衡

代理模式虽然强大且用途广泛,但在实际设计和应用中,也存在一些需要注意的陷阱和权衡,并不是所有场景都适合引入代理层。

  • 引入额外的复杂性: 这是最直接的代价。一个简单的函数调用,现在可能需要经过代理层、权限检查、延迟初始化等步骤。这意味着更多的类、更多的文件、更多的间接性。对于非常简单的场景,这种额外的封装可能显得过度设计,反而增加了理解和维护的成本。有时,一个简单的

    if
    条件判断就能解决的问题,没必要上升到模式层面。

  • 性能开销: 尽管虚拟代理旨在优化性能,但代理本身在每次方法调用时都会引入一个小的开销(例如,检查

    RealSubject
    是否为空,执行权限判断)。对于高频调用的、对性能极其敏感的方法,即使是很小的额外开销也可能累积成明显的性能瓶颈。在设计时,需要仔细评估这种间接性带来的性能影响是否可以接受。

  • 调试难度增加: 当出现问题时,调用堆栈会变得更深,因为请求需要经过代理再到达真实对象。这可能会使调试变得稍微复杂,需要花费更多时间来追踪代码执行路径。

  • 接口同步的挑战: 代理和真实对象必须实现相同的接口。如果真实对象的接口发生变化(例如,添加、删除或修改方法),那么代理也必须相应地更新。这在大型项目中,如果接口频繁变动,可能会成为一个维护负担,容易引入不一致性。这要求我们在设计接口时,尽量保持其稳定性。

  • 过度设计: 就像所有设计模式一样,代理模式也可能被过度使用。并非所有需要“控制访问”或“延迟加载”的场景都非得用代理模式。有时候,一个简单的工厂方法、一个装饰器模式,甚至是直接的条件逻辑,可能更直接、更易于理解和维护。设计模式是工具,不是目的。我们应该在真正需要解决特定问题时才考虑使用它,而不是为了用模式而用模式。

  • 生命周期管理: 代理持有真实对象的引用,正确管理真实对象的生命周期至关重要。使用

    std::unique_ptr
    或
    std::shared_ptr
    是C++中推荐的做法,但仍然需要确保在代理被销毁时,真实对象能够被正确释放,尤其是在涉及循环引用或复杂所有权关系时。

总的来说,代理模式提供了一种优雅的方式来解耦和增强功能,但它并非银弹。在决定是否引入代理时,我个人会更倾向于问自己:“这种间接性带来的好处(如性能提升、安全增强、职责分离)是否显著地超过了它引入的复杂性和潜在的性能损耗?”很多时候,答案是肯定的,尤其是在面对大型、复杂的系统时。但如果一个问题有更简单、更直接的解决方案,那么简单往往是更好的选择。

热门AI工具

更多
二狗PPT
二狗PPT Hot

一款AI演示文稿工具,主要用于专为中式职场打造的AI PPT生成工具,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

PixPix
PixPix Hot

PixPix是一款面向电商视觉生产的AI商品图生成工具。

咔片AIPPT

一款在线AI演示文稿制作工具,可根据主题和内容需求辅助生成PPT结构与页面,提高演示材料制作效率。

墨刀AI
墨刀AI Hot

一款AI图像与设计工具,主要用于产品经理的专属智能体,适合需要提升相关任务效率的用户。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

SkildArt
SkildArt Hot

SkildArt是一款AI文本写作工具,一站式 AI 视觉创作平台。

Seko
Seko Hot

一款AI视频创作工具,主要用于商汤科技推出的创编一体的AI短视频创作Agent,适合需要提升相关任务效率的用户。

相关专题

更多
C++ 智能指针与现代内存管理
C++ 智能指针与现代内存管理

深入讲解 C++ 现代内存管理的核心工具——智能指针,涵盖 unique_ptr 独占所有权语义、shared_ptr 引用计数机制与循环引用问题、weak_ptr 弱引用的应用场景、make_unique/make_shared 工厂函数的性能优势、自定义删除器的编写、RAII 资源管理思想的实践,以及从裸指针迁移到智能指针的重构策略,帮助开发者编写安全无泄漏的现代 C++ 代码。

359

2026.04.23

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

3148

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

4949

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

3789

2025.12.29

java接口相关教程
java接口相关教程

本专题整合了java接口相关内容,阅读专题下面的文章了解更多详细内容。

446

2026.01.19

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

5907

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

2528

2023.08.10

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

5907

2023.07.18

剧云故事大纲与分场规划教程
剧云故事大纲与分场规划教程

本专题讲解剧云梗概与多级大纲功能,覆盖全局、分集、分场结构生成,以及主线冲突、剧情节点和场次顺序调整。

0

2026.10.22

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
SQL 教程
SQL 教程

共61课时 | 7.3万人学习

Gradle 安全最佳实践
Gradle 安全最佳实践

共0课时 | 0人学习

Gradle Wrapper 使用与安全指南
Gradle Wrapper 使用与安全指南

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn