分析一套源代碼的代碼規範和風格並討論如何改進優化代碼——高級軟件工程課第三次做業

結合工程實踐的選題,我選擇的是一個開源的C++輕量級網絡框架——ZLToolKit。下面按照所給的要求依次展開(如下均以Google的C++編碼規範爲標準)mysql

1.根據其編程語言或項目特色,分析其在源代碼目錄結構、文件名/類名/函數名/變量名等命名、接口定義規範和單元測試組織形式等方面的作法和特色算法

  src文件夾下的源代碼目錄結構以下:sql

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), 枚舉等均使用相同約定。
 該源碼一些類型的代碼以下所示,能夠看出類型命名基本依照現有的標準。
  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));

  能夠看到,該源碼中函數的命名主要依據的時駝峯式的命名法,但並未統一,時而依照駝峯命名法,時而所有小寫,不符合標準。windows

  變量名:按照標準,變量名一概小寫,單詞用下劃線相連,例如: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), 枚舉等均使用相同約定。app

 2.3 該源碼函數的命名不符合一致性的要求

 2.4 該源碼變量名基本符合Google的C++標準 

 

3.列舉哪些作法有悖於「代碼的簡潔、清晰、無歧義」的基本原則,及如何進一步優化改進

 1.源碼總體註釋較少,遠低於註釋要佔代碼20%的要求,尤爲是對宏的註釋,基本沒有。改進:添加註釋——每一條宏都要加註釋;在函數定義的開頭添加註釋以說明該函數的做用

 2.源碼中部分使用了宏定義函數,這是C++不提倡的作法。改進:使用內聯函數代替宏函數

 

4.總結同類編程語言或項目在代碼規範和風格的通常要求

 代碼風格因人而異,最重要的是保持本身代碼風格從一而終的一致性。但無可厚非,有一些出自大廠的且你們都承認代碼風格規範是值得借鑑和學習的,由於它符   合大多數人的閱讀習慣,如下根據相關代碼規範對代碼的基本書寫列出一些通常要求:

 1.命名:

  文件名所有小寫;函數名所有按照單詞首字母大寫;普通變量名一概小寫;成員變量名小寫且以_爲結束做爲標記。

 2.空行

  定義變量後要空行

  每一個函數定義結束以後都要加空行

  兩個相對獨立的程序塊之間要空行

 3.空格

  函數名以後不要留空格

  (向後緊跟;),;這三個向前緊跟;緊跟處不留空格  

  值運算符、關係運算符、算術運算符、邏輯運算符、位運算符等先後應當加空格

  單目運算符先後不加空格

 4.縮進

  若是地位相等,則不須要縮進;若是屬於某一個代碼的內部代碼就須要縮進。

 5.註釋

  注意註釋的數量,註釋太多會讓人眼花繚亂

  當代碼比較長,特別是有多重嵌套的時候,應當在段落的結束處加註釋

 每一條宏定義的右邊必需要有註釋,說明其做用  
相關文章
相關標籤/搜索