分析一套源代码的代码规范和风格并讨论如何改进优化代码——高级软件工程课第三次作业

结合工程实践的选题,我选择的是一个开源的C++轻量级网络框架——ZLToolKit。下面按照所给的要求依次展开(以下均以Google的C++编码规范为标准)

1.根据其编程语言或项目特点,分析其在源代码目录结构、文件名/类名/函数名/变量名等命名、接口定义规范和单元测试组织形式等方面的做法和特点

  src文件夹下的源代码目录结构如下:

src
|
|-- NetWork				# 网络模块
|	|-- Socket.cpp			# 套接字抽象封装,包含了TCP服务器/客户端,UDP套接字
|	|-- Socket.h
|	|-- sockutil.cpp		# 系统网络相关API的统一封装
|	|-- sockutil.h
|	|-- TcpClient.cpp		# TCP客户端封装,派生该类可以很容易实现客户端程序
|	|-- TcpClient.h
|	|-- TcpLimitedSession.h 	# 派生于TcpSession,该模板类可以全局限制会话数量
|	|-- TcpServer.h			# TCP服务器模板类,可以很容易就实现一个高性能私有协议服务器
|	|-- TcpSession.h 		# TCP服务私有协议实现会话基类,用于处理TCP长连接数据及响应
|
|-- Poller				# 主线程事件轮询模块
|	|-- EventPoller.cpp		# 主线程,所有网络事件由此线程轮询并触发
|	|-- EventPoller.h
|	|-- Pipe.cpp			# 管道的对象封装
|	|-- Pipe.h
|	|-- PipeWrap.cpp		# 管道的包装,windows下由socket模拟
|	|-- SelectWrap.cpp		# select 模型的简单包装 
|	|-- SelectWrap.h
|	|-- Timer.cpp			# 在主线程触发的定时器
|	|-- Timer.h
|
|-- Thread				# 线程模块
|	|-- AsyncTaskThread.cpp		# 后台异步任务线程,可以提交一个可定时重复的任务后台执行
|	|-- AsyncTaskThread.h
|	|-- rwmutex.h			# 读写锁,实验性质的
|	|-- semaphore.h			# 信号量,由条件变量实现
|	|-- spin_mutex.h		# 自旋锁,在低延时临界区适用,单核/低性能设备慎用
|	|-- TaskQueue.h			# functional的任务列队
|	|-- threadgroup.h		# 线程组,移植自boost
|	|-- ThreadPool.h		# 线程池,可以输入functional任务至后台线程执行
|	|-- WorkThreadPool.cpp		# 获取一个可用的线程池(可以加入线程负载均衡分配算法)
|	|-- WorkThreadPool.h
|
|-- Util				# 工具模块
	|-- File.cpp			# 文件/目录操作模块
	|-- File.h
	|-- function_traits.h		# 函数、lambda转functional
	|-- logger.h			# 日志模块
	|-- MD5.cpp			# md5加密模块
	|-- MD5.h
	|-- mini.h			# ini配置文件读写模块,支持unix/windows格式的回车符
	|-- NoticeCenter.h		# 消息广播器,可以广播传递任意个数任意类型参数
	|-- onceToken.h			# 使用RAII模式实现,可以在对象构造和析构时执行一段代码
	|-- ResourcePool.h		# 基于智能指针实现的一个循环池,不需要手动回收对象
	|-- RingBuffer.h		# 环形缓冲,可以自适应大小,适用于GOP缓存等
	|-- SqlConnection.cpp		# mysql客户端
	|-- SqlConnection.h
	|-- SqlPool.h			# mysql连接池,以及简单易用的sql语句生成工具
	|-- SSLBox.cpp			# openssl的黑盒封装,屏蔽了ssl握手细节,支持多线程
	|-- SSLBox.h
	|-- TimeTicker.h		# 计时器,可以用于统计函数执行时间
	|-- util.cpp			# 其他一些工具代码,适配了多种系统
	|-- util.h
	|-- uv_errno.cpp		# 提取自libuv的错误代码系统,主要是为了兼容windows
	|-- uv_errno.h

 可以看到,源码的模块划分主要以原作者设计实现时的功能模块划分为导向。
 文件名:按照Google的C++编码标准,文件名应当全部使用小写。而该源码文件命名较为随意,一些采用大写,一些采用小写,不符合规范要求
 类型名:按照Google的C++编码标准,类型命名应当每个单词首字母大写,不含下划线,以名词形式,且所有类型命名 —— 类, 结构体, 类型定义 (typedef), 枚举等均使用相同约定。
 该源码一些类型的代码如下所示,可以看出类型命名基本依照现有的标准。
  
class SocketFlags{
public:
    SocketFlags(int flags):_flags(flags){};
    ~SocketFlags(){}
    int _flags;
};

class MutexWrapper {
public:
    MutexWrapper(bool enable){
        _enable = enable;
    }
    ~MutexWrapper(){}

    inline void lock(){
        if(_enable){
            _mtx.lock();
        }
    }
    inline void unlock(){
        if(_enable){
            _mtx.unlock();
        }
    }
private:
    bool _enable;
    Mtx _mtx;
};

typedef enum {
	Err_success = 0, //成功
	Err_eof, //eof
	Err_timeout, //超时
	Err_refused,//连接别拒绝
	Err_dns,//dns解析失败
	Err_shutdown,//主动关闭
	Err_other = 0xFF,//其他错误
} ErrCode;

  函数名:按照标准,常规函数每个单词首字母大写,使用命令式语气,比如:OpenFile()   CheckFileName(),而存取函数或短小的内联函数使用小写加下    划线,且与访问变量相吻合,比如 set_num_errors()。该源码的部分函数声明或调用如下

  

void Socket::setOnErr(const onErrCB &cb);
void Socket::setOnAccept(const onAcceptCB &cb);

void Socket::setOnFlush(const onFlush &cb);

//设置Socket生成拦截器
void Socket::setOnBeforeAccept(const onBeforeAcceptCB &cb);

setsockopt(sockFd, IPPROTO_IP, IP_DROP_MEMBERSHIP,  (char*)&imr, sizeof (struct ip_mreq));

  可以看到,该源码中函数的命名主要依据的时驼峰式的命名法,但并未统一,时而依照驼峰命名法,时而全部小写,不符合标准。

  变量名:按照标准,变量名一律小写,单词用下划线相连,例如:int player_id; string table_name;特殊的是类成员变量,后跟下划线区别普通变量,比     player_name_   player_id_。该源码的变量命名基本按照了小写的要求,类中的变量命名也通过下划线与普通变量区别开来:

  

class SockException: public std::exception {
public:
	SockException(ErrCode errCode = Err_success,
                   const string &errMsg = "",
                   int customCode = 0) {
        _errMsg = errMsg;
        _errCode = errCode;
        _customCode = customCode;

    }

    //重置错误
	void reset(ErrCode errCode, const string &errMsg) {
		_errMsg = errMsg;
        _errCode = errCode;
	}
    //错误提示
	virtual const char* what() const noexcept {
		return _errMsg.c_str();
	}
    //错误代码
	ErrCode getErrCode() const {
		return _errCode;
	}
    //判断是否真的有错
	operator bool() const{
		return _errCode != Err_success;
	}
    //用户自定义错误代码
    int getCustomCode () const{
        return _customCode;
    }
    //获取用户自定义错误代码
    void setCustomCode(int code) {
        _customCode = code;
    };
private:
	string _errMsg;
	ErrCode _errCode;
    int _customCode = 0;
};

 

2.列举哪些做法符合代码规范和风格一般要求 

 根据上面的分析,我们可以看出:

 2.1 该源码文件名的命名规则不符合Google的C++标准中规定的——文件名应当全部使用小写。

 2.2 该源码的类型名的命名规则基本符合Google的C++标准中规定的——类型命名应当每个单词首字母大写,不含下划线,以名词形式,且所有类型命名 —— 类, 结构体, 类型定义 (typedef), 枚举等均使用相同约定。

 2.3 该源码函数的命名不符合一致性的要求

 2.4 该源码变量名基本符合Google的C++标准 

3.列举哪些做法有悖于“代码的简洁、清晰、无歧义”的基本原则,及如何进一步优化改进

 1.源码整体注释较少,远低于注释要占代码20%的要求,尤其是对宏的注释,基本没有。改进:添加注释——每一条宏都要加注释;在函数定义的开头添加注释以说明该函数的作用

 2.源码中部分使用了宏定义函数,这是C++不提倡的做法。改进:使用内联函数代替宏函数

4.总结同类编程语言或项目在代码规范和风格的一般要求

 代码风格因人而异,最重要的是保持自己代码风格从一而终的一致性。但无可厚非,有一些出自大厂的且大家都认可代码风格规范是值得借鉴和学习的,因为它符   合大多数人的阅读习惯,以下根据相关代码规范对代码的基本书写列出一些一般要求:

 1.命名:

  文件名全部小写;函数名全部按照单词首字母大写;普通变量名一律小写;成员变量名小写且以_为结束作为标记。

 2.空行

  定义变量后要空行

  每个函数定义结束之后都要加空行

  两个相对独立的程序块之间要空行

 3.空格

  函数名之后不要留空格

  (向后紧跟;),;这三个向前紧跟;紧跟处不留空格  

  值运算符、关系运算符、算术运算符、逻辑运算符、位运算符等前后应当加空格

  单目运算符前后不加空格

 4.缩进

  如果地位相等,则不需要缩进;如果属于某一个代码的内部代码就需要缩进。

 5.注释

  注意注释的数量,注释太多会让人眼花缭乱

  当代码比较长,特别是有多重嵌套的时候,应当在段落的结束处加注释

 每一条宏定义的右边必须要有注释,说明其作用  
原文地址:https://www.cnblogs.com/hhssqq9999/p/11649314.html