結合工程實踐的選題,我選擇的是一個開源的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++標準中規定的——類型命名應當每一個單詞首字母大寫,不含下劃線,以名詞形式,且全部類型命名 —— 類, 結構體, 類型定義 (
apptypedef
), 枚舉等均使用相同約定。
2.3 該源碼函數的命名不符合一致性的要求
2.4 該源碼變量名基本符合Google的C++標準
3.列舉哪些作法有悖於「代碼的簡潔、清晰、無歧義」的基本原則,及如何進一步優化改進
1.源碼總體註釋較少,遠低於註釋要佔代碼20%的要求,尤爲是對宏的註釋,基本沒有。改進:添加註釋——每一條宏都要加註釋;在函數定義的開頭添加註釋以說明該函數的做用
2.源碼中部分使用了宏定義函數,這是C++不提倡的作法。改進:使用內聯函數代替宏函數
4.總結同類編程語言或項目在代碼規範和風格的通常要求
代碼風格因人而異,最重要的是保持本身代碼風格從一而終的一致性。但無可厚非,有一些出自大廠的且你們都承認代碼風格規範是值得借鑑和學習的,由於它符 合大多數人的閱讀習慣,如下根據相關代碼規範對代碼的基本書寫列出一些通常要求:
1.命名:
文件名所有小寫;函數名所有按照單詞首字母大寫;普通變量名一概小寫;成員變量名小寫且以_爲結束做爲標記。
2.空行
定義變量後要空行
每一個函數定義結束以後都要加空行
兩個相對獨立的程序塊之間要空行
3.空格
函數名以後不要留空格
(
向後緊跟;)
、,
、;
這三個向前緊跟;緊跟處不留空格
值運算符、關係運算符、算術運算符、邏輯運算符、位運算符等先後應當加空格
單目運算符先後不加空格
4.縮進
若是地位相等,則不須要縮進;若是屬於某一個代碼的內部代碼就須要縮進。
5.註釋
注意註釋的數量,註釋太多會讓人眼花繚亂
當代碼比較長,特別是有多重嵌套的時候,應當在段落的結束處加註釋
每一條宏定義的右邊必需要有註釋,說明其做用