檔案:
高品質C++程式設計補充條款.pdf
大小:
169KB
下載下傳:
<a href="http://blogimg.chinaunix.net/blog/upfile2/101107130137.pdf">下載下傳</a>
目錄
<a href="#_Toc7199%20">1. 前言 1</a>
<a href="#_Toc24027%20">2. 條款:避免使用非衆所周知的縮略語 1</a>
<a href="#_Toc24765%20">3. 條款:規範好#include 1</a>
<a href="#_Toc25157%20">4. 條款:避免長短語句無規律交錯 2</a>
<a href="#_Toc22481%20">5. 條款:避免頭重腳輕 2</a>
<a href="#_Toc32432%20">6. 條款:充分利用public和private等 3</a>
<a href="#_Toc20925%20">7. 條款:類成員優先使用對象類型 4</a>
<a href="#_Toc9991%20">8. 條款:名字空間的使用 5</a>
<a href="#_Toc2952%20">9. 條款:巧用do...while(false)替代goto 5</a>
<a href="#_Toc471%20">10. 條款:利用typedef增強代碼的自注釋 7</a>
<a href="#_Toc165%20">11. 條款:不要失去對程序和線程的控制權 7</a>
介紹高品質C++程式設計的書籍很多,而且都非常好,這裡主要針對已有書籍較少涉及到的代碼格式條款進行補充。代碼是程式員臉面,清清爽爽和幹幹淨淨的代碼是程式員高職業素質的展現,清爽的代碼需要從細節做起,用心呵護。
本條款非新鮮的,但實際很少有人真正遵循,在代碼中總能見到一些自創的縮略語,後來接手代碼的人常常需要去猜測是啥意思。是以提出來重點強調一下,如:GeneralServer,不要寫成CGSConfig,而應當使用長名CGeneralServerConfig,長名是自注釋的,前者在上下文環境不足和缺少文檔的情況(除了華為那樣文檔要求非常嚴格的企業,可能一般公司都存在這樣的情況)下可能需要去猜測。
寫#include也有講究,通常在前,""在後,如:
#include
#include "mooon.h"
而且非隸屬本編譯工程中的頭檔案,一律使用,隸屬本編譯工程中的頭檔案使用""。
下面這段代碼無規律的交錯着,容易給人以混亂的感覺:
void reset_current_message(bool finish);
void free_current_message();
void inc_resend_times();
util::handle_result_t do_handle_reply();
void clear_message();
net::epoll_event_t do_send_message(void* ptr, uint32_t events);
使用這一條款後,變成成如下:
void clear_message();
void inc_resend_times();
void free_current_message();
void reset_current_message(bool finish);
util::handle_result_t do_handle_reply();
net::epoll_event_t do_send_message(void* ptr, uint32_t events);
從短到長,明顯清爽清晰了很多,變量了定義等也應當盡量遵守此條款。
#include段也應當盡量遵循這個規律,如:
#include "sys/fs_util.h"
#include "sys/close_helper.h"
如果一些變量是相關的,則可以使用空行分開,在同一組内實施這一條款。
char* str = get_value("thread_number");
if (str != NULL)
{
thread_number = string2int(str);
if (0 == thread_number)
_thread_number = 1;
else
_thread_number = thread_number;
}
上面的代碼段,就顯得頭重腳輕,if塊比else塊大了很多。特别是當if塊超過50行時,會導緻else塊較難看,甚至可能難以一下确定else對應哪個if語句。将兩者跌倒一下,就可以消除頭重腳輕的問題,如下:
if (NULL == str)
C++允許public等修飾符在一個類的定義中多次重複出現,充分利用這一特性,可使得類的定義代碼變得更清爽。下面這段代碼充分利用了這一特性,對類的定義進行了歸類,使得整個定義顯得較為清爽不淩亂交錯:
class CSender: public net::CTcpClient
{
public: // 公有函數
~CSender();
CSender(CSendThreadPool* thread_pool, int32_t route_id, uint32_t queue_max, IReplyHandler* reply_handler);
int32_t get_node_id() const;
bool push_message(dispatch_message_t* message, uint32_t milliseconds);
public: // 公有的虛拟函數
virtual void after_connect();
private: // 重寫的虛拟函數
virtual void before_close();
virtual void connect_failure();
private: // 非重寫的私有函數
protected: // 提供給不同子類使用的公共函數
void do_set_resend_times(int8_t resend_times);
net::epoll_event_t do_handle_epoll_event(void* ptr, uint32_t events);
private: // 非狀态成員
int32_t _route_id;
CSendQueue _send_queue;
IReplyHandler* _reply_handler;
CSendThreadPool* _thread_pool;
private: // 發送狀态相關的
int8_t _cur_resend_times; // 目前已經連續重發的次數
int8_t _max_resend_times; // 失敗後最多重發的次數,負數表示永遠重發,0表示不重發
uint32_t _current_offset; // 目前已經發送的位元組數
dispatch_message_t* _current_message; // 目前正在發送的消息
};
按照UML上的術語來說,就是優先使用組合,而非聚合,雖然從依賴性上講聚合低于組合,但這隻是理論上,對于一個對象的生命周期由别一個類來掌握時,使用組合更好,原因是組合使得該類對象的記憶體空間連續,而聚合通常需要在構造函數中new,在析構中delete,容易造成更多的記憶體碎片,總是連續的比非連續的好,如:
class CAgentThread
private:
CMasterConnector _connector; // 建議使用對象類型
當然如果隻是關聯關系,那肯定隻能使用指針類型了,如:
CAgentContext* _contexnt; // 隻能使用指針類型
杜絕在頭檔案使用using,包括using namespace std和using std::vector兩種形式。這樣做完全失去了名字空間的意義,減少名字間的沖突。
先看下段代碼:
int CTcpClient::timed_connect()
int fd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == fd)
return errno; // goto CONNECT_ERROR:
if (-1 == connect(fd, peer_addr, addr_length))
close(fd);
return errno;
if (!CNetUtil::timed_poll(fd, POLLIN | POLLOUT | POLLERR, _milli_seconds))
if (-1 == getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &errcode_length))
set_fd(fd);
return 0;
上面這段代碼,在出錯的地方,有多處return,代碼基本相同,通常大家會想到使用goto語句來解決這個問題。goto總是應當隻作為最後不得已的一種選擇,通過下面這段代碼我們來看看如何使用do...while(false)優雅的解決這個問題:
int fd = -1;
do
fd = socket(AF_INET, SOCK_STREAM, 0);
break; // goto CONNECT_ERROR:
break;
while (false);
// 相當于goto到這裡
if (fd != -1) close(fd);
使用do...while(false)後,整個函數就隻有兩個return出口了。
在一些開源和C++标準庫stl中,可以見到大量的typedef使用,除了使用typedef來簡化長類型的定義,如:typedef basic_string string;外,還有增強代碼自注釋的目的。
假設需要一個存儲IP端口号的清單,可以定義如下:
std::list port_list_t;
在設計和代碼中,應當杜絕時長未定或較長的sleep調用,以及完全阻塞的accept/read等調用,因為這會使你失去對程序和線程的控制權。當你需要進行死鎖檢測,将不容易區分,當程式需要退出,會比較麻煩。正确的做法是保證sleep的時間盡可能短而且最長時間明确,通常不要超過10秒,甚至可以考慮使用可喚醒的條件等替代,而accept/read應當改用帶逾時的,或使用非阻塞的,這樣就能牢牢把握對程序和線程的控制權。
待續 。。。。。。