news 2026/5/5 7:12:08

跟我学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/5/2 0:36:07

全网最全8个一键生成论文工具,自考党轻松搞定毕业论文!

全网最全8个一键生成论文工具&#xff0c;自考党轻松搞定毕业论文&#xff01; AI 工具如何让论文写作更轻松 在当前的学术环境中&#xff0c;越来越多的学生开始借助 AI 工具来完成论文写作任务。这些工具不仅能够帮助用户快速生成内容&#xff0c;还能有效降低 AIGC 率&#…

作者头像 李华
网站建设 2026/5/1 9:28:31

OGG音频也能处理:小众格式用户的福音来了

OGG音频也能处理&#xff1a;小众格式用户的福音来了 在数字人技术逐渐走入日常的今天&#xff0c;一个看似微不足道的技术细节&#xff0c;往往能决定用户体验的“最后一公里”是否顺畅。比如——你有没有遇到过这样的情况&#xff1a;手头有一段从会议录音、语音助手或WebRTC…

作者头像 李华
网站建设 2026/5/2 18:06:10

从数组到Span:提升数据转换效率300%,你还在用传统方式吗?

第一章&#xff1a;从数组到Span&#xff1a;性能变革的起点在现代高性能计算场景中&#xff0c;数据访问效率直接决定系统吞吐能力。传统的数组操作虽然简单直观&#xff0c;但在跨方法传递、内存复制和边界检查方面存在显著开销。.NET 引入的 Span<T> 类型正是为解决此…

作者头像 李华
网站建设 2026/4/24 4:28:14

Transformer Key Concepts

Types of position encoding Types of masks in transformer Role of sqrt(d_model) in transformer PreNorm vs PostNorm RmsNorm vs other forms of normalization

作者头像 李华
网站建设 2026/5/3 9:21:27

【C#跨平台调试终极指南】:掌握这5大技巧,效率提升200%

第一章&#xff1a;C#跨平台调试的现状与挑战随着 .NET Core 升级为 .NET 5 及更高版本&#xff0c;C# 应用的跨平台能力得到了显著增强。开发者能够在 Windows、Linux 和 macOS 上构建和运行相同的应用程序&#xff0c;但随之而来的调试复杂性也日益凸显。开发环境碎片化 不同…

作者头像 李华