news 2026/6/10 20:44:05

跟我学C++中级篇——链式调用的实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
跟我学C++中级篇——链式调用的实践

一、链式调用

在前面的文章中,已经对链式调用有了不少的了解。比如“ this指针”和“设计模式”以及C++23中显式this等文章中都多少有些涉及。但实际上,C++对链式调用的支持并不多给力。如果有Java特别是Kotlin语言开发经验的,对链式调用应该是非常容易理解的。
所谓链式调用,就是像链表一样,将函数的调用连接起来,即可以连续调用多个函数。它让代码看起来更直白、易维护。不过如果调用链太深,反而让开发者有点感觉到莫名其妙。这也是事物的两面性吧。链式调用更符合自然语言,所以在一些函数编程语言以及高级语言中应用非常广泛。C++中的链式调用应用并没有其它语言那么广泛,这也是C++语言本身的一些特点和应用场景限制的。

二、运行机制和原理

链式调用的原理本质是对对象引用或指针的控制和处理。链式调用需要在每次完成后继续调用相关的函数,就必须得到函数所在的对象,进而才能够进行下一步的函数调用。所以链式调用的核心机制就是保证必须能够通过上一个函数调用返回应用对象的引用或指针,从而确保连续调用的函数影响作用到对象的同一实例(某些扩展实现可能不是这种情况)。

三、实现方式

实现链式调用对于C++这类语言来说并不复杂,但也并如函数式语言那么简单。其主要的实现方式包括:

  1. 对象引用和指针的操作
    在函数的返回值中通过返回指针(this)或 引用(*this)的方式来传回同一对象实例。这是最常用的方法:
class Demo{public:Demo*setColor(intv){color_=v;returnthis;}Demo*setHigh(intv){high_=v;returnthis;}private:intcolor_=0;inthigh_=160;};intmain(){Demo d;d.setColor(255)->setHigh(180);return0;}
  1. 流畅接口实现(Fluent Interface)
    流畅式接口的设计实现其实更倾向于从逻辑上对链式调用的实现,实际实现并未脱离链式调用实现的基本方法。以一个电商的操作为例:
#include<iostream>#include<string>#include<vector>class OnShoppingCart{private:std::vector<std::string>itemName_;std::string userName_;public:OnShoppingCart&getUser(conststd::string&userName){userName_=userName;return*this;}OnShoppingCart&addItem(conststd::string&itemName){itemName_.push_back(itemName);return*this;}};intmain(){OnShoppingCart cart;cart.getUser("iPad").addItem("iPhone").addItem("iWatch");return0;}
  1. 运算符重载实现
    这种实现非常常见,比如std::cout中对<<操作符的重载,看下面的简单例子:
#include<string>class Demo{public:Demo*setColor(intv){color_=v;returnthis;}Demo*setHigh(intv){high_=v;returnthis;}Demo&operator<<(conststd::string&msg){msg_+=msg;return*this;}private:intcolor_=0;inthigh_=160;std::string msg_="";};intmain(){Demo d;d<<"hello "<<"world!";return0;}
  1. 模板中的CRTP实现
    这个在前面有专门的论述,可参看相关“CRTP”的文章,下面看例子:
template<typename ConcretePrinter>class Printer{public:Printer(std::ostream&pstream):stream_(pstream){}template<typename T>ConcretePrinter&print(T&&t){stream_<<t;returnstatic_cast<ConcretePrinter&>(*this);}template<typename T>ConcretePrinter&println(T&&t){stream_<<t<<std::endl;returnstatic_cast<ConcretePrinter&>(*this);}private:std::ostream&stream_;};enumColor{red,blue,green};class CoutPrinter:public Printer<CoutPrinter>{public:CoutPrinter():Printer(std::cout){}CoutPrinter&SetConsoleColor(Color c){return*this;}};voidTestChain(){CoutPrinter().print("Hello ").SetConsoleColor(Color::red).println("Printer!");}intmain(){TestChain();return0;}

CRTP对于大多数的开发者可能觉得有点陌生,不想深入学习模板技术的可以只知道有这么一回事即可,不必深究。

四、应用场景

链式调用的应用场景其实也不算少,主要有:

  1. 设计模式中的应用
    比如常见的建造者模式、流畅接口模式等。
  2. 异步调用
    在异步调用中可以使用链式调用来处理回调,让代码更简洁和方便
  3. 发布-订阅机制
    通过事件驱动消息的链式调用发送

链式调用优点明显但也有不少的缺点,典型的就是链式调用过程中出现异常的处理比较复杂,另外一个就是调试过程中复杂的来回跳转,增加了调试中的困难。这些大家要根据情况自行评估应用。

五、例程

在上面学习的基础上,看一个比较典型的观察者模式中对事件通知的处理:

#include<iostream>#include<functional>#include<vector>#include<string>#include<memory>#include<algorithm>class Observer{public:virtual~Observer()=default;virtualvoidonEvent(conststd::string&event,conststd::string&task)=0;};class TaskObserver:public Observer{private:std::string runnerName_;public:TaskObserver(conststd::string&name):runnerName_(name){}voidonEvent(conststd::string&event,conststd::string&task)override{std::cout<<runnerName_<<"Event: "<<event<<", task: "<<task<<std::endl;}};class EventControl{private:std::vector<std::shared_ptr<Observer>>observers_;public:EventControl&insertObserver(std::shared_ptr<Observer>ob){observers_.push_back(ob);return*this;}EventControl&delObserver(std::shared_ptr<Observer>ob){autoit=std::remove(observers_.begin(),observers_.end(),ob);observers_.erase(it,observers_.end());return*this;}EventControl&notify(conststd::string&event,conststd::string&task=""){for(constauto&ob:observers_){ob->onEvent(event,task);}return*this;}EventControl&clear(){observers_.clear();return*this;}};intmain(){EventControl control;autoworkderA=std::make_shared<TaskObserver>("workderA");autoworkderB=std::make_shared<TaskObserver>("workderB");control.insertObserver(workderA).insertObserver(workderB).notify("task1","start eating...").notify("task2","start eating the soup...").delObserver(workderA).notify("finish","all finished!").clear();return0;}

六、总结

链式调用作为C++中一种比较优雅的设计方式对于提高项目整体设计和开发的简洁性有着很重要的帮助,同时其良好的维护性和可扩展性也为后续的开发提供了方便的接口实现。但其本身所固有的一些问题也是比较突出的,这就需要设计和开发者根据自己的实际需求进行权衡应用。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 10:33:54

批量删除选中功能限制:最多同时清除多少项?

批量删除选中功能限制&#xff1a;最多同时清除多少项&#xff1f; 在数字人视频生成系统日益普及的今天&#xff0c;用户面对的不再只是单次任务的执行效率&#xff0c;而是成百上千条生成记录的管理难题。HeyGem 作为一款基于大模型驱动的音视频合成平台&#xff0c;在提供高…

作者头像 李华
网站建设 2026/6/10 16:27:20

学校强制穿校服的成本 - 收益分析:多主体视角下的理性权衡

学校强制穿校服的成本 - 收益分析&#xff1a;多主体视角下的理性权衡学校要求学生穿校服的政策&#xff0c;本质是一场涉及学校、学生、家长三方的成本 - 收益博弈。其核心争议在于 “短期显性成本”&#xff08;如校服费用、个性限制&#xff09;与 “长期隐性收益”&#xf…

作者头像 李华
网站建设 2026/6/10 12:50:54

C#集合表达式性能实战(高性能LINQ编写秘籍)

第一章&#xff1a;C#集合表达式性能概览C# 中的集合表达式&#xff08;Collection Expressions&#xff09;是 C# 12 引入的一项重要语言特性&#xff0c;允许开发者使用简洁的语法创建不可变集合实例。这类表达式在编译时会被优化为高效的 IL 代码&#xff0c;显著减少运行时…

作者头像 李华
网站建设 2026/6/9 23:12:38

揭秘C# 12拦截器工作机制:5分钟彻底搞懂拦截器配置核心原理

第一章&#xff1a;C# 12拦截器机制概述C# 12 引入的拦截器机制是一项实验性功能&#xff0c;旨在为方法调用提供一种轻量级的拦截能力&#xff0c;允许开发者在不修改原始方法代码的前提下&#xff0c;插入自定义逻辑。该特性主要面向 AOP&#xff08;面向切面编程&#xff09…

作者头像 李华
网站建设 2026/6/10 14:42:20

FLV老视频还能用!传统格式用户也能接入HeyGem生态

FLV老视频还能用&#xff01;传统格式用户也能接入HeyGem生态 在不少教育机构的服务器角落里&#xff0c;可能还静静躺着成千上万个FLV格式的旧课程录像——这些曾伴随在线教育起步的“数字遗产”&#xff0c;在过去几年几乎成了技术演进中的弃子。Flash的落幕让FLV被贴上“过时…

作者头像 李华
网站建设 2026/6/10 14:43:47

紧急应对C#服务部署异常:3种高危场景及实时回滚方案

第一章&#xff1a;C#企业系统部署异常概述在企业级应用开发中&#xff0c;C#凭借其强大的生态系统和与Windows平台的深度集成&#xff0c;被广泛应用于后端服务、桌面程序及Web系统的构建。然而&#xff0c;在实际部署过程中&#xff0c;系统可能因环境差异、配置错误或依赖缺…

作者头像 李华