罗剑锋的 C++ 实战笔记
罗剑锋
前奇虎 360 技术专家,Nginx/OpenResty 开源项目贡献者
35514 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 32 讲
结束语 (1讲)
罗剑锋的 C++ 实战笔记
15
15
1.0x
00:00/00:00
登录|注册

22 | 知识串讲(下):带你开发一个书店应用

minmax_sales()函数
自旋锁保护核心数据
容器类型选择
销售记录存储
pack()函数
MSGPACK_DEFINE
使用noexcept标记不抛出异常,优化函数
使用const来修饰常函数
using定义类型别名
成员变量在声明时直接初始化
使用委托构造来编写多个不同形式的构造函数
使用default显示定义拷贝构造、拷贝赋值、转移构造、转移赋值等重要函数
使用final终结类继承体系,不允许别人产生子类
在编译阶段使用静态断言,保证整数、浮点数的精度
类名使用CamelCase,函数和变量用snake_case,成员变量加“m_”前缀
适当使用空行分隔代码里的逻辑段落
HTTP客户端发送请求
JSON序列化数据
读取Lua配置中的HTTP服务器地址和周期运行时间
存储数据
MessagePack反序列化
ZMQ接收数据
Summary类
MessagePack
C++编码准则
销售金额
销售册数
ID号
把前端与服务器的数据交换格式改成JSON或者ProtoBuf
写一个动态库,用Lua/Python调用C++发送请求,以脚本的方式简化客户端测试
添加try-catch,处理可能发生的异常
使用lambda表达式时要注意捕获变量的生命周期
存储数据时选择恰当的容器
序列化格式选择
编写类的时候要用好final、default、using、const等关键字
log_cycle
服务器主循环
数据存储类Summary
加载配置文件
数据存储与统计
序列化
SalesData类
课下作业
小结
数据外发线程
服务端主线程
数据的表示与统计
应用的底层基础
Lua配置文件解析
自旋锁
类图
项目设计
知识串讲:带你开发一个书店应用

该思维导图由 AI 生成,仅供参考

你好,我是 Chrono。
在上节课里,我给出了一个书店程序的例子,讲了项目设计、类图和自旋锁、Lua 配置文件解析等工具类,搭建出了应用的底层基础。
今天,我接着讲剩下的主要业务逻辑部分,也就是数据的表示与统计,还有数据的接收和发送主循环,最终开发出完整的应用程序。
这里我再贴一下项目的 UML 图,希望给你提个醒。借助图形,我们往往能够更好地把握程序的总体结构。
图中间标注为绿色的两个类 SalesData、Summary 和两个 lambda 表达式 recv_cycle、log_cycle 是今天这节课的主要内容,实现了书店程序的核心业务逻辑,所以需要你重点关注它。

数据定义

首先,我们来看一下怎么表示书本的销售记录。这里用的是 SalesData 类,它是书店程序数据统计的基础。
如果是实际的项目,SalesData 会很复杂,因为一本书的相关信息有很多。但是,我们的这个例子只是演示,所以就简化了一些,基本的成员只有三个:ID 号、销售册数和销售金额。
上节课,在讲自旋锁、配置文件等类时,我只是重点说了说代码内部逻辑,没有完整地细说,到底该怎么应用前面讲过的那些 C++ 编码准则。
那么,这次在定义 SalesData 类的时候,我就集中归纳一下。这些都是我写 C++ 代码时的“惯用法”,你也可以在自己的代码里应用它们,让代码更可读可维护:
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文详细介绍了开发书店应用的主要业务逻辑,包括数据表示与统计、数据接收和发送主循环等内容。通过对SalesData类和Summary类的设计和实现,读者可以了解如何应用C++编码准则和选择合适的数据结构来开发书店应用的核心业务逻辑。文章还介绍了服务端主线程和数据外发线程的实现,包括使用lambda表达式和智能指针来处理数据接收和发送,以及使用HTTP协议将数据发送到后台的RESTful服务器。作者强调了在编写类时应用关键字和细节来提高效率和安全性,选择合适的序列化格式和容器,以及注意捕获变量的生命周期和异常处理。总体而言,本文展示了如何利用C++特性实现多线程、高性能的服务端程序,并提供了一些实践性的课下作业,帮助读者巩固所学知识。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《罗剑锋的 C++ 实战笔记》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(17)

  • 最新
  • 精选
  • robonix
    代码里显式声明了转移构造和转移赋值函数,这样,在放入容器的时候就避免了拷贝,能提高运行效率。 那么被转移的类会被掏空了,使得内部数据无效吗?

    作者回复: 需要理解转移语义,它的目的就是要把原对象的内容给“偷走”,转移到新的对象里。 这样原对象就空了,但数据依然是有效的,比如0、nullptr,只是没有了实际意义,可以被安全、轻量地销毁。

    2020-06-28
    4
  • wine99
    老师课外小贴士中说的“使用了泛型的lambda”,是指lambda的入口参数用了auto吗?

    作者回复: 是的,泛型的lambda就是以auto作为参数类型,让编译器来推导类型。

    2021-10-03
    3
  • robonix
    老师,还想问一下,为啥不用std::lock_guard,自己写一个lock呢,只为了性能嘛?

    作者回复: 示例代码,当然都是自己写出来比较好了,可以实践一下编码准则。

    2020-06-28
    3
  • Geek_6427cc
    老师您好,想向您请教下,HTTP 服务器地址URL中的token参数有什么作用,在您编写的Lua模块中,判断了这个token参数,不是很理解

    作者回复: 这个token就是一个非常简单的鉴权认证,如果token里不是字符串cpp@2020,就直接返回403,可以防止其他应用误入这个uri。 如果接触过api网关、restful调用,可能就会理解它的作用了。

    2021-09-07
    2
    2
  • Geek_8866d4
    SalesData(SalesData&& s) noexcept 罗老师, 你这个引用的引用怎么理解呢,我的c++语言功底比较薄弱,您的这个代码我很多都不是很理解

    作者回复: &&表示右值引用,意思是对象是临时的,可以被move优化。 这个函数是C++11引入的转移构造函数,构造的时候可以把函数参数s里的东西都给“偷”到自己内部,降低对象创建的成本。 C++里的概念很多,如果遇到暂时不了解的也不要着急,说明你可能暂时用不上,探索其他更有用的地方,不要为一两个小特性分心。 有其他不明白的可以再问。

    2021-04-18
    2
  • 有学识的兔子
    1、Thread生成的地方,没有去做异常检查,我不太确定需不需要? 2、假如使用python脚本去简化客户端测试,是不是通过PYBIND11的方式把Client.cpp里的接口转化成python能够加载模块,在利用python测试该模块? 3.可以将SalesData里面涉及pack和upack的部分拆分出来,用工厂方法进行替换;工厂类可以借用STL将不同类型数据格式和对应工厂类映射起来;在通过配置文件增加该类型的配置,解析到数据类型后,关联到对应的工厂类产生对应的对象,基于此来动态切换实现pack和unpack的数据格式。

    作者回复: 1.只要没有显式声明noexcept的地方,其实都应该加上try-catch。 2.对,用C++写底层接口,然后用Python、lua去调用。 3.思路很对。

    2020-06-26
    2
  • 忧天小鸡
    代码全都看得懂,但我不是服务端啊,这个怎么运行起来,缺少这方面的知识,怎么办?

    作者回复: 用g++编译生成可执行文件,源码里有注释可以参考。

    2021-12-17
    1
  • 阿辉
    recv_cycle的for循环的目的是什么?为啥,我运行完成程序,这个for循环只在client执行的时候会执行!因为我打了日志,只有client执行后,才会输出相应日志!

    作者回复: recv_cycle是服务主循环,首先阻塞在监听端口,有请求过来后才能走后面的流程处理数据。 参考一下代码里的注释,或者看看其他服务器的代码,应该就能理解了。

    2021-07-11
    1
  • 木瓜777
    每次接受请求,都开启一个线程,是否合理?

    作者回复: 每个请求开新线程的代价是比较高的,但课程里的代码只是为了演示目的,实际项目里最好用线程池。

    2020-06-25
    2
    1
  • 起床君
    补充2个点: 1. ZmqContext里的静态成员函数:zmq_socket_type send_sock(int hwm = 1000, int linger = 10),client端如果用这个函数做发送,建议把linger值设大一点,或者直接设为-1,这样客户端不会因为立刻关闭,而丢失掉mq中的消息;否则由于没有报错,服务端收不到消息也不好排查~ 2. SalesData中的成员变量m_revenue,看意思应该是想设为double类型(using currency_type = double;),虽然实际中也不会用double,不过也算是个小typo啦~

    作者回复: good

    2023-09-14归属地:新加坡
收起评论
显示
设置
留言
17
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部