webserver服务器从零搭建到上线(五)|noncopyable类和Logger类

news/2024/7/25 2:55:39/文章来源:https://blog.csdn.net/caiziming_001/article/details/138509662

文章目录

  • noncopyable类
    • delete掉了拷贝构造和析构
    • protected成员
      • 1. 允许派生
      • 2.防止直接实例化
    • 主要使用场景
  • Logger类
    • 定义日志级别
    • 输出一个日志类
    • 实现对应的成员函数
    • 实现宏函数来调用日志类
  • 知识拓展

noncopyable类

我们首先进入/muduo/net中查看TcpServer.h、EventLoop.h等等核心代码的类,都继承子一个基类noncopyable,那么我们不得不去看一下noncopyable到底是啥了

class noncopyable
{public:noncopyable(const noncopyable&) = delete;void operator=(const noncopyable&) = delete;protected:noncopyable() = default;~noncopyable() = default;
};

这个类他把它的拷贝构造函数和拷贝复制运算符都delete掉了;另外他写了一个默认default的构造函数和析构函数,相当于就是给直接套了一个空的大括号,因为这两个函数编译器本来就可以自动生成。

delete掉了拷贝构造和析构

我们在使用该基类的时候,去对TcpServer的对象进行拷贝构造和赋值的时候,由于派生类的拷贝和赋值要先去调用基类的拷贝和赋值,然后才是派生类特有部分的拷贝和赋值。

但是由于基类的拷贝和赋值都被delete掉了,所以它的好处就在于我们的其他类如果不想让他被拷贝或者复制,就不用额外写这段代码了,这是一个大型程序所必须的

总结

  • 防止派生类的拷贝构造

protected成员

1. 允许派生

protected 访问修饰符使得只有 noncopyable 类及其派生类能够访问这些构造和析构函数。这意味着不能直接在 noncopyable 类的外部创建该类的实例(即不能直接实例化 noncopyable),但您可以创建派生自 noncopyable 的类的实例。

2.防止直接实例化

如果构造函数和析构函数是 public 的,那么理论上可以直接创建和销毁 noncopyable 类的对象,这违背了类的设计初衷(作为一个基类来阻止复制)。通过将构造和析构函数设为 protectednoncopyable 类的实例化只能通过其子类进行,而 noncopyable 本身不能被直接实例化

主要使用场景

有一个管理资源的类,且这些资源不应该被复制时(比如说,单例模式、文件处理类、数据库连接管理类),您可以让这个类继承自 noncopyable,从而禁止编译时对这些资源进行复制操作

Logger类

日志对于一个软件来说是非常重要的,如果软件上线,用gdb调试有很多不便,所以我们应该使用日志来记录问题。

定义日志级别

定义日志级别一般都是使用枚举类。

enum LogLevel {INFO,ERROR,FATAL,DEBUG,
};

输出一个日志类

日志类应该输出单例模式:

class Logger: noncopyable {
public://获取日志唯一的实例对象static Logger& instance();//设置日志级别void setLogLevel(int level);//写日志的接口void log(std::string msg);
private:int logLevel_;Logger() { }
}

为什么要使用单例模式呢?

  1. 全局访问点:单例模式确保在整个应用程序中只有一个日志类实例,方便统一管理和访问。
  2. 资源共享:避免多个日志实例导致的资源浪费,特别是文件句柄和网络连接等有限资源。
  3. 一致性:确保日志配置和行为一致,避免不同模块使用不同的日志实例导致的日志风格不一致。

实现对应的成员函数

Logger& Logger::instance() { //获取日志唯一的实例对象static Logger logger;return logger;
}void Logger::setLogLevel(int level) {// 设置日志级别logLevel_ = level;
}void Logger::log(std::string msg) {//写日志的接口  【级别信息】time : xxxswitch (logLevel_){case INFO:std::cout << "[INFO]";break;case ERROR:std::cout << "[ERROR]";break;case FATAL:std::cout << "[FATAL]";break;case DEBUG:std::cout << "[DEBUG]";break;default:break;}//打印时间和msgstd::cout << Timestamp::now().toString() << ": " << msg << std::endl;
}

这里的Timestamp类在「webserver服务器从零搭建到上线(六)」会进行介绍。

实现宏函数来调用日志类

用的时候我们不需要让用户来操作这些复杂的步骤:

  • 获取日志实例
  • 设置日志级别
  • 在写日志

我们难道还让用户调用这么多接口吗?
用户真正关心的就是写日志,打印出来,所以我们应该把这些内容全部包装到宏函数里面。

这里定义四种宏,对应4个日志级别

//LOG_INFO("%s %d", arg1, arg2)
//参数 ... 表示可变参
#define LOG_INFO(logmsgFormat, ...) \do { \Logger &logger = Logger::instance(); \logger.setLogLevel(INFO); \char buf[1024] = { 0 }; \snprintf(buf, 1024, logmsgFormat, ##__VA_ARGS__); \logger.log(buf); \} while(0)

在宏定义中,##__VA_ARGS__是一种预处理器语法,用于处理可变参数宏。


#define LOG_ERROR(logmsgFormat, ...) \do { \Logger &logger = Logger::instance(); \logger.setLogLevel(ERROR); \char buf[1024] = { 0 }; \snprintf(buf, 1024, logmsgFormat, ##__VA_ARGS__); \logger.log(buf); \exit(-1); \} while(0)#define LOG_FATAL(logmsgFormat, ...) \do { \Logger &logger = Logger::instance(); \logger.setLogLevel(FATAL); \char buf[1024] = { 0 }; \snprintf(buf, 1024, logmsgFormat, ##__VA_ARGS__); \logger.log(buf); \} while(0)

在ERROR级别应该直接停止程序运行。

最后我们重点谈一下 LOG_DEBUG

#ifdef MUDEBUG
#define LOG_DEBUG(logmsgFormat, ...) \do { \Logger &logger = Logger::instance(); \logger.setLogLevel(DEBUG); \char buf[1024] = { 0 }; \snprintf(buf, 1024, logmsgFormat, ##__VA_ARGS__); \logger.log(buf); \} while(0)
#else#define LOG_DEBUG(logmsgFormat, ...)
#endif

其中的 #ifdef 可以保证我们在release版本中,不定义宏 MUDEBUG,就可以不打印调试信息,毕竟日志属于IO操作,对想能影响较大。

知识拓展

在实现宏函数来调用日志类中我们使用了 snprintf ,它是 C 标准库中的一个函数,用于将格式化输出写入字符串,限制写入的最大字符数以避免缓冲区溢出。它是 sprintf 的安全版本。

int snprintf(char *str, size_t size, const char *format, ...);

参数:
str:指向目标缓冲区的指针。
size:要写入的最大字符数,包括终止空字符。
format:格式字符串,类似于 printf 的格式说明。
…:可变参数,根据格式字符串进行格式化。

使用日志类是通过使用宏函数:

LOG_INFO("func=%s => fd total count:%lu \n", __FUNCTION__, channels_.size());

其中的__FUNCTION__表示当前函数的名称。这个宏是由编译器提供的,帮助开发者在调试和记录日志时自动插入当前函数的名称,便于追踪和排查问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_1052353.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【全开源】JAVA情侣扭蛋机情侣游戏系统源码支持微信小程序+微信公众号+H5

一、引言&#xff1a;增添恋爱甜蜜&#xff0c;扭出惊喜时刻 在恋爱生活中&#xff0c;总是需要一些小小的惊喜和乐趣来增添甜蜜。为此&#xff0c;我们精心打造了“情侣扭蛋机情侣游戏系统小程序源码”&#xff0c;为情侣们提供一个充满趣味和创意的互动平台。 二、独特扭蛋…

C++的第一道门坎:类与对象(一)

1.面向过程与面向对象 1.1面向过程 我们之前学习的C语言就是一种面向过程的语言&#xff0c;面向过程的语言强调的是具体实现的过程&#xff0c;一般用函数来具体实现。我们用面向过程的思想&#xff0c;就可以把炒菜分为以下几个步骤: 1.2面向对象 而对于面向对象的语言而言…

【ARMv8/v9 异常模型入门及渐进 10 -- WFI 与 WFE 使用详细介绍 1】

请阅读【ARMv8/v9 ARM64 System Exception】 文章目录 WFI 与 WFE等待事件&#xff08;WFE&#xff09;发送事件&#xff08;SEV&#xff09;本地发送事件&#xff08;SEVL&#xff09;WFE 唤醒事件 WFE 使用场景举例与代码实现wfe睡眠函数sev 事件唤醒函数全局监视器和自旋锁 …

如何异地组网添加摄像机?

本文将介绍如何使用天联技术实现异地组网添加摄像机&#xff0c;并保障数据的安全性。 安防摄像机的应用愈发广泛&#xff0c;无论是家庭安防还是企业监控&#xff0c;摄像机都扮演着重要角色。在一些特殊场合或者特殊需求下&#xff0c;我们需要将摄像机添加到异地网络中进行监…

uniapp android使用uni.chooseLocation,app云打包后,定位地址列表一直在加载中

复现BUG 1、自己生成一个证书 参考生成证书流程 2、使用刚生成证书的SHA1 &#xff0c;重新创建一个高德key 高德开放平台地址 3、打包&#xff08;打包的包名要与高德申请key所填的包名一致&#xff09;

Java面试八股之Synchronized锁升级的原理

Synchronized锁升级的原理 Synchronized锁升级是Java为了提高并发性能而引入的一项优化措施&#xff0c;这一机制主要发生在JDK 1.6及之后的版本中。Synchronized锁升级旨在减少锁带来的性能开销&#xff0c;通过从低开销的锁逐步升级到高开销的锁&#xff0c;以适应不同的竞争…

从零入门激光SLAM(二十一)——FAST-LIO2论文解析

FAST-LIO2: Fast Direct LiDAR-Inertial Odometry 论文地址&#xff1a;https://ieeexplore.ieee.org/stamp/stamp.jsp?tp&arnumber9697912 代码&#xff1a;https://github.com/hku-mars/FAST_LIO 一、文章概述 1.问题导向 基于视觉传感器的高分辨率和高精度的实时密…

Docker笔记-搭建达梦Python环境(dmPython + SQLAlchemy)

背景 达梦提供的C接口&#xff0c;dpi和java的jar包已经很好用了&#xff0c;想不到&#xff0c;来了一个用python的同事&#xff0c;这里就只能适应下他了&#xff0c;在不影响其他环境下搭建一个python的达梦环境。最后发现&#xff0c;python对进行达梦增删改查&#xff0c…

【设计模式】JAVA Design Patterns——Bytecode(字节码模式)

&#x1f50d;目的 允许编码行为作为虚拟机的指令 &#x1f50d;解释 真实世界例子 一个团队正在开发一款新的巫师对战游戏。巫师的行为需要经过精心的调整和上百次的游玩测试。每次当游戏设计师想改变巫师行为时都让程序员去修改代码这是不妥的&#xff0c;所以巫师行为以数据…

1 计算机硬件-CPU-校验码-存储系统-输入输出设备-总线结构

计算机硬件 考情分析&#xff1a;趋势很小&#xff0c;22年考过&#xff0c;根据趋势以后考的可能较小 基本组成&#xff1a;运算器&#xff0c;控制器&#xff0c;储存器&#xff0c;输入设备&#xff0c;输出设备运算器和控制器也统称为中央处理单元&#xff08;CPU&#xf…

视频截图软件,这几款截图神器收好!

在数字化时代&#xff0c;视频内容已经成为我们获取信息、娱乐休闲的主要方式之一。而在观看视频的过程中&#xff0c;我们总会遇到一些想要定格下来的精彩瞬间。此时&#xff0c;一款高效的视频截图软件就显得尤为重要。今天&#xff0c;就为大家推荐几款功能强大、操作简便的…

2024年5月软考,别再傻傻啃书了!

备考2024年软考&#xff0c;不听课也不刷题&#xff0c;只是看教材的话&#xff0c;想要考试通过&#xff0c;几乎是不可能的&#xff0c;特别是基础比较薄弱的考生。 为什么只看教材通不过&#xff1f; 如果只是把教材从头到尾看一遍&#xff0c;毫无目的地看书&#xff0c;…

【傻呱呱】VirtualHere共享局域网中的USB设备(使用Pavadan老毛子固件搭建篇)

前期准备 SSH工具&#xff08;FinalShell&#xff09;老毛子固件路由器一台 搭建VirtualHere服务端 进入VirtualHere官网下载对应处理器架构的包&#xff0c;我的是RT-N14U-GPIO路由器刷的老毛子固件&#xff0c;这种一般选择最后一个或者倒数第二个包&#xff0c;这里我选择…

Java三种方法实现多线程,继承Thread类,实现Runnable接口,实现Callable接口

目录 线程&#xff1a; 继承Thread类&#xff1a; 实现Runnable类&#xff1a; 实现Callable接口&#xff1a; 验证多线程&#xff1a; 线程&#xff1a; 定义&#xff1a;进程可以同时执行多个任务&#xff0c;每个任务就是线程。举个例子&#xff1a;一个Java程序&#…

23种设计模式顺口溜

口诀&#xff1a; 原型 抽风 &#xff0c;单独 建造 工厂 &#xff08;寓意&#xff1a;&#xff08;这里代指本来很简单的东西&#xff0c;却要干工厂这里复杂的业务&#xff09; 抽风&#xff1a;抽象工厂单独&#xff1a;单例桥代理组合享元适配器&#xff0c;&#xff0…

Modbus协议基础

文章目录 Modbus概述Modbus TCP/IP Modbus概述 Modbus是由Modicon&#xff08;现为施耐德电气公司的一个品牌&#xff09;在1979年发明的&#xff0c;是全球第一个真正用于工业现场的应用层总线协议。 为更好地普及和推动Modbus在基于以太网上的分布式应用&#xff0c;目前施…

[STM32-HAL库]ADC采集-DMA中断采集-平均值滤波-STM32CUBEMX开发-HAL库开发系列-主控STM32F103C8T6

目录 一、前言 二、实现步骤 1.STM32CUBEMX配置 2.Keil工程程序设计 三、结语 一、前言 本文通过STM32CUBEMX实现对ADC的数据采集和滤波操作&#xff0c;帮助各位开发者完成与模拟量输入的采集工作。 二、实现步骤 1.STM32CUBEMX配置 以STM32F103C8T6为例&#xff0c;打开S…

微信小程序上传包过大的最全解决方案!

微信小程序的发布大小限制是2MB。然而一个程序怎么能这么小&#xff1f; 介绍一下项目中的经验。 新项目 如果是刚开始做的新项目&#xff0c;一定确定好自己要用的Ui框架&#xff0c;而且确定之后&#xff0c;千万不要引入别的&#xff0c;否则占大小&#xff01;&#xff0…

6.Redis之String命令

1.String类型基本介绍 redis 所有的 key 都是字符串, value 的类型是存在差异的~~ 一般来说,redis 遇到乱码问题的概率更小~~ Redis 中的字符串,直接就是按照二进制数据的方式存储的. (不会做任何的编码转换【讲 mysql 的时候,知道 mysql 默认的字符集, 是拉丁文,插入中文…

sheng的学习笔记-AI-EM算法

AI学习笔记目录&#xff1a;sheng的学习笔记-AI目录-CSDN博客 目录 基础知识 什么是EM算法 EM算法简介 数学知识 极大似然估计 问题描述 用数学知识解决现实问题 最大似然函数估计值的求解步骤 Jensen不等式 定义 EM算法详解 问题描述 EM算法推导流程 EM算法流程…