天天看點

高品質C++程式設計補充條款 1. 前言 2. 條款:避免使用非衆所周知的縮略語 3. 條款:規範好#include 4. 條款:避免長短語句無規律交錯 5. 條款:避免頭重腳輕 6. 條款:充分利用public和private等 7. 條款:類成員優先使用對象類型 8. 條款:名字空間的使用 9. 條款:巧用do...while(false)替代goto 10. 條款:利用typedef增強代碼的自注釋 11. 條款:不要失去對程序和線程的控制權

檔案:

高品質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, &amp;errcode, &amp;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應當改用帶逾時的,或使用非阻塞的,這樣就能牢牢把握對程序和線程的控制權。

待續 。。。。。。

繼續閱讀