0


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

文章目录

noncopyable类

我们首先进入/muduo/net中查看TcpServer.h、EventLoop.h等等核心代码的类,都继承子一个基类

noncopyable

,那么我们不得不去看一下

noncopyable

到底是啥了

classnoncopyable{public:noncopyable(const noncopyable&)=delete;voidoperator=(const noncopyable&)=delete;protected:noncopyable()=default;~noncopyable()=default;};

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

delete掉了拷贝构造和析构

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

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

总结

  • 防止派生类的拷贝构造

protected成员

1. 允许派生

protected

访问修饰符使得只有

noncopyable

类及其派生类能够访问这些构造和析构函数。这意味着不能直接在

noncopyable

类的外部创建该类的实例(即不能直接实例化

noncopyable

),但您可以创建派生自

noncopyable

的类的实例。

2.防止直接实例化

如果构造函数和析构函数是

public

的,那么理论上可以直接创建和销毁

noncopyable

类的对象,这违背了类的设计初衷(作为一个基类来阻止复制)。通过将构造和析构函数设为

protected

,**

noncopyable

类的实例化只能通过其子类进行,而

noncopyable

本身不能被直接实例化**。

主要使用场景

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

Logger类

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

定义日志级别

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

enumLogLevel{
    INFO,
    ERROR,
    FATAL,
    DEBUG,};

输出一个日志类

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

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

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

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

实现对应的成员函数

Logger&Logger::instance(){//获取日志唯一的实例对象static Logger logger;return logger;}voidLogger::setLogLevel(int level){// 设置日志级别
    logLevel_ = level;}voidLogger::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;}//打印时间和msg
    std::cout <<Timestamp::now().toString()<<": "<< msg << std::endl;}

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

实现宏函数来调用日志类

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

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

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

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

//LOG_INFO("%s %d", arg1, arg2)//参数 ... 表示可变参#defineLOG_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__

是一种预处理器语法,用于处理可变参数宏。


#defineLOG_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)#defineLOG_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

#ifdefMUDEBUG#defineLOG_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#defineLOG_DEBUG(logmsgFormat,...)#endif

其中的

#ifdef

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

知识拓展

intsnprintf(char*str, size_t size,constchar*format,...);

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

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

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

其中的

__FUNCTION__

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


本文转载自: https://blog.csdn.net/caiziming_001/article/details/138509662
版权归原作者 Che3rry 所有, 如有侵权,请联系我们删除。

“webserver服务器从零搭建到上线(五)|noncopyable类和Logger类”的评论:

还没有评论