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

C++如何使用策略模式封装算法行为

落辰姑娘_3627

落辰姑娘_3627

发布时间:2025-09-11 11:26:01

|

812人浏览过

|

来源于php中文网

原创

策略模式通过封装算法家族并使其可互换,实现算法与客户端的解耦。1. 定义抽象策略接口;2. 创建具体策略类实现算法;3. 上下文持有策略接口指针,运行时动态切换具体策略;4. 利用C++多态性,通过虚函数实现运行时绑定,结合智能指针管理生命周期,提升扩展性与维护性。

c++如何使用策略模式封装算法行为

C++中,策略模式(Strategy Pattern)的核心思想是定义一系列算法家族,将每个算法封装起来,并使它们可以互相替换,从而让算法的变化独立于使用算法的客户端。简单来说,它将算法抽象为一个接口或基类,让具体的算法实现这个接口,然后通过一个上下文(Context)对象持有这个接口的引用,在运行时根据需要切换不同的算法实现。这就像你给一个机器人换不同的程序卡片,让它执行不同的任务,而机器人本身不需要知道每张卡片具体是怎么工作的。

解决方案

要使用策略模式封装C++中的算法行为,我们需要定义三个主要角色:

  1. 抽象策略(Strategy):这是一个接口或抽象基类,声明了所有具体策略类都必须实现的方法。它定义了算法的公共接口。
  2. 具体策略(Concrete Strategy):实现抽象策略接口的具体算法类。每个具体策略类都代表一个特定的算法。
  3. 上下文(Context):持有对抽象策略对象的引用,并委托该策略对象执行算法。上下文不直接实现算法,而是将算法的执行委派给当前设置的策略对象。

以下是一个简单的C++示例,展示如何使用策略模式来处理不同类型的支付方式:

#include <iostream>
#include <memory> // For std::unique_ptr

// 1. 抽象策略(Strategy)
// 定义所有支付策略的公共接口
class PaymentStrategy {
public:
    virtual ~PaymentStrategy() = default; // 虚析构函数很重要!
    virtual void pay(double amount) const = 0;
};

// 2. 具体策略(Concrete Strategy)
// 实现具体的支付算法
class CreditCardPayment : public PaymentStrategy {
public:
    void pay(double amount) const override {
        std::cout << "使用信用卡支付了 " << amount << " 元。" << std::endl;
        // 这里可以加入信用卡支付的具体逻辑,比如调用第三方API
    }
};

class PayPalPayment : public PaymentStrategy {
public:
    void void pay(double amount) const override {
        std::cout << "使用PayPal支付了 " << amount << " 元。" << std::endl;
        // 这里可以加入PayPal支付的具体逻辑
    }
};

class BankTransferPayment : public PaymentStrategy {
public:
    void pay(double amount) const override {
        std::cout << "使用银行转账支付了 " << amount << " 元。" << std::endl;
        // 这里可以加入银行转账的具体逻辑
    }
};

// 3. 上下文(Context)
// 持有策略对象,并委托其执行算法
class ShoppingCart {
private:
    std::unique_ptr<PaymentStrategy> paymentStrategy;
    double totalAmount;

public:
    ShoppingCart(double amount) : totalAmount(amount) {}

    // 设置支付策略
    void setPaymentStrategy(std::unique_ptr<PaymentStrategy> strategy) {
        paymentStrategy = std::move(strategy);
    }

    // 执行支付
    void checkout() const {
        if (paymentStrategy) {
            paymentStrategy->pay(totalAmount);
        } else {
            std::cout << "未设置支付策略,无法结账。" << std::endl;
        }
    }
};

// 客户端代码
int main() {
    ShoppingCart cart(100.50);

    // 使用信用卡支付
    cart.setPaymentStrategy(std::make_unique<CreditCardPayment>());
    cart.checkout();

    std::cout << "--------------------" << std::endl;

    // 切换到PayPal支付
    cart.setPaymentStrategy(std::make_unique<PayPalPayment>());
    cart.checkout();

    std::cout << "--------------------" << std::endl;

    // 切换到银行转账支付
    cart.setPaymentStrategy(std::make_unique<BankTransferPayment>());
    cart.checkout();

    return 0;
}

C++策略模式:为什么它是算法封装的明智之选?

在C++项目中,当我们面对那些可能随着时间推移而变化、或者有多种实现方式的算法时,策略模式往往是一个非常优雅且实用的解决方案。它不像简单的函数封装那样,仅仅是把代码块挪个位置;策略模式更侧重于将“行为”抽象化,并使得这些行为可以独立于使用它们的“主体”而变化。

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

首先,它带来了解耦的巨大好处。算法的实现细节被封装在独立的策略类中,客户端代码(比如上面的

ShoppingCart
)完全不需要知道支付的具体流程是刷卡还是转账,它只知道调用一个
pay()
方法就行。这种松散耦合让系统各部分职责明确,互不干扰。我个人觉得,这种“不知道细节,只管用”的感觉,是软件设计中追求的最高境界之一。

其次,可扩展性是策略模式的另一个亮点。如果未来需要增加一种新的支付方式,比如加密货币支付,我们只需要创建一个新的

CryptoPayment
类,实现
PaymentStrategy
接口,然后就可以直接在
ShoppingCart
中使用,而无需修改任何现有代码。这完美符合了“开闭原则”(对扩展开放,对修改关闭),大大降低了维护成本和引入bug的风险。试想一下,如果没有策略模式,你可能得在
ShoppingCart
里写一大堆
if-else if
或者
switch-case
来判断支付类型,每次新增一种类型就得修改这个巨大的条件分支,那简直是噩梦。

再者,它提升了代码的可维护性和可读性。每个具体策略类只负责一种算法,代码量相对较小,逻辑清晰。当出现问题时,你可以快速定位到具体的算法实现,而不是在一个庞大的函数中苦苦寻找。这对于团队协作和长期项目维护来说,简直是福音。

最后,也是非常实用的一点,它允许运行时动态切换算法。在上面的例子中,用户可以在结账时选择不同的支付方式,

ShoppingCart
可以根据用户的选择,动态地设置不同的
PaymentStrategy
。这种灵活性是传统硬编码算法所无法比拟的。在我自己的项目经验中,很多时候业务需求的不确定性,使得我们必须预留这种“运行时可变”的能力,而策略模式就是为此而生。

C++策略模式实现细节与潜在挑战解析

在C++中实现策略模式,虽然核心思想清晰,但有一些细节和潜在挑战是需要我们特别注意的,尤其是在资源管理和设计选择上。

Crypto Arbitrage Monitor
Crypto Arbitrage Monitor

加密货币跨交易所套利监控器,实时监控BTC/ETH/SOL等主流币在各交易所价差,发现套利机会后通过飞书/Telegram预警,自动计算扣除手续费后的净利润。

下载

最关键的一点是抽象基类与虚函数。

PaymentStrategy
这样的抽象基类,必须声明至少一个纯虚函数(
= 0
),这样它就不能被实例化,只能作为接口使用。同时,虚析构函数是不可或缺的。如果
Context
通过基类指针管理
Strategy
对象的生命周期(比如
std::unique_ptr<PaymentStrategy>
),那么当
Context
销毁时,如果
PaymentStrategy
没有虚析构函数,那么在删除
ConcreteStrategy
对象时,只会调用基类的析构函数,而不会调用派生类的析构函数,这会导致内存泄漏和未定义行为。这是C++多态性编程中一个经典的“坑”,一定要牢记。

关于上下文与策略的生命周期管理,这是C++特有的一个重要考量。

Context
如何持有
Strategy
对象?

  1. 原始指针:最简单,但容易出错,需要手动管理内存,可能导致悬空指针或内存泄漏。
  2. 引用:
    Context
    不负责
    Strategy
    对象的生命周期,外部必须保证
    Strategy
    对象在
    Context
    使用期间一直有效。
  3. 智能指针(如
    std::unique_ptr
    或
    std::shared_ptr
    ):这是C++11及以后版本推荐的做法。
    • std::unique_ptr
      :表示
      Context
      拥有
      Strategy
      对象的唯一所有权。当
      Context
      被销毁时,它会自动销毁持有的
      Strategy
      对象。这是最常见的选择,因为它清晰地表达了所有权关系。
    • std::shared_ptr
      :如果多个
      Context
      实例可能共享同一个
      Strategy
      对象,或者
      Strategy
      的生命周期需要被其他对象共同管理,那么
      std::shared_ptr
      是合适的。但这会增加一些运行时开销,并且可能引入循环引用等问题。 在上面的示例中,我使用了
      std::unique_ptr
      ,因为它清晰地表明了
      ShoppingCart
      拥有其当前的支付策略。

策略的参数传递也是一个常见问题。算法通常需要一些数据才能执行。这些数据可以从

Context
传递给
Strategy
的执行方法(如
pay(double amount)
),或者在创建
Strategy
对象时通过构造函数注入。选择哪种方式取决于数据的性质:如果数据是算法执行的动态输入,每次调用都可能不同,那么通过方法参数传递更合适;如果数据是算法配置的一部分,在策略的整个生命周期内相对固定,那么通过构造函数注入会更干净。

最后,一个需要警惕的陷阱是过度设计。不是所有的算法都需要策略模式。如果一个算法非常简单,变化的可能性微乎其微,或者你的系统里根本就没有其他替代算法,那么引入策略模式可能会增加不必要的复杂性。它会增加类和接口的数量,使得代码追踪变得稍微复杂一些。策略模式的价值在于处理“变化”,如果“变化”不存在,那么它的优势也就无从谈起。在项目初期,我们经常需要在“简单快速”和“灵活可扩展”之间做权衡。

策略模式与C++多态性的深度融合

策略模式在C++中的实现,可以说就是C++运行时多态性的一个教科书式应用。理解它们之间的关系,能帮助我们更深入地掌握这两种强大的编程概念。

C++的多态性,特别是运行时多态,是通过虚函数和虚函数表(vtable)机制实现的。当一个基类指针或引用指向一个派生类对象时,通过这个指针或引用调用虚函数时,实际执行的是派生类中对应的实现。这正是策略模式能够工作的基石。在我们的支付例子中,

ShoppingCart
对象持有一个
PaymentStrategy
类型的智能指针,当它调用
paymentStrategy->pay(amount)
时,C++的运行时机制会根据
PaymentStrategy
实际指向的具体策略对象(如
CreditCardPayment
或
PayPalPayment
),来调用正确的
pay
方法。

这种机制的强大之处在于,

ShoppingCart
完全不需要知道它当前正在使用的是哪种具体支付方式。它只知道它有一个
PaymentStrategy
,并且这个
PaymentStrategy
有一个
pay
方法。这种“面向接口编程”的理念,正是策略模式所推崇的。它使得我们的代码更加抽象,更少地依赖于具体的实现细节。

此外,我们前面强调的虚析构函数,也是多态性在资源管理中的一个关键体现。如果基类析构函数不是虚的,那么通过基类指针删除派生类对象时,多态性机制不会生效,只会调用基类的析构函数,导致派生类特有的资源无法正确释放。这在C++中是一个非常常见且隐蔽的错误源。

当然,C++还有编译期多态,比如通过模板(Template)实现。有时,人们也会尝试用模板来实现类似策略模式的效果,比如“Policy-based design”或者使用CRTP(Curiously Recurring Template Pattern)。这种方式可以避免虚函数调用的运行时开销,获得更高的性能。但它通常意味着策略是在编译时确定的,无法在运行时动态切换,且模板代码的复杂性有时会更高。经典的策略模式主要依赖于运行时多态,它的优势在于其灵活性和对“行为可变性”的优雅处理,尤其是在业务逻辑复杂且多变的场景下。很多时候,我们写C++代码,一旦涉及到“行为可变”,自然而然就会想到多态,策略模式就是这种思维模式的一个经典体现,它把这种“可变性”结构化了,使得我们可以清晰地管理和扩展这些变化的行。

热门AI工具

更多
立刻MV
立刻MV Hot

立刻MV是一款AI文本写作工具,AI 音乐视频(MV)创作工具。

音述AI
音述AI Hot

一款AI音频处理工具,主要用于音述AI是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区,适合需要提升相关任务效率的用户。

创客贴AI

创客贴AI是一款AI图像与设计工具,AI辅助的智能在线设计工具。

Laper
Laper Hot

Laper是专为编剧、导演和制片人推出的 AI 原生剧本创作工具。

PixPix
PixPix Hot

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

豆包大模型

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

WorkBuddy

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

咔片AIPPT

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

DeepSeek

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

相关专题

更多
switch语句用法
switch语句用法

switch语句用法:1、Switch语句只能用于整数类型,枚举类型和String类型,不能用于浮点数类型和布尔类型;2、每个case语句后面必须跟着一个break语句,以防止执行其他case的代码块,没有break语句,将会继续执行下一个case的代码块;3、可以在一个case语句中匹配多个值,使用逗号分隔;4、Switch语句中的default代码块是可选的等等。

1071

2023.09.21

Java switch的用法
Java switch的用法

Java中的switch语句用于根据不同的条件执行不同的代码块。想了解更多switch的相关内容,可以阅读本专题下面的文章。

1417

2024.03.13

java多态详细介绍
java多态详细介绍

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

355

2025.11.27

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

4188

2025.08.29

C++中int、float和double的区别
C++中int、float和double的区别

本专题整合了c++中int和double的区别,阅读专题下面的文章了解更多详细内容。

804

2025.10.23

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

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

379

2026.04.23

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

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

3188

2023.10.19

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

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

5069

2025.10.17

Gradle插件开发入门教程
Gradle插件开发入门教程

本专题介绍Gradle插件开发的基本流程,涵盖插件类编写、自定义任务注册、扩展参数设计和插件应用方法,帮助你理解插件如何组织构建逻辑,减少多个项目之间的重复配置。

100

2026.10.22

热门下载

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

精品课程

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

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