動態

詳情 返回 返回

我的C++代碼整潔之道,你也能做到! - 動態 詳情

記得剛入行時一位老哥就跟我説“代碼是寫給人看的”,我很認同這句話,但是直到如今也不敢説自己能完全做到,即便如此我依然有些心得體會,於是便有了本文。

實踐一: struct僅用於數據封裝。
參考示例

struct Point
{
    int _x = 0;
    int _y = 0;
};

實踐二: 類名採用帕斯卡命名法(大駝峯),使用名詞或名詞短語命名,除非專有名詞或廣泛被使用的縮寫(如 URL、ID、HTTP、IP),否則儘量用完整單詞。

參考示例

class Student {};
class UserManager {};
class HttpServer {};

實踐三: 函數使用動詞或動詞短語遵守帕斯卡命名法(大駝峯)命名,應明確説明其動作或用途,除非專有名詞或廣泛被使用的縮寫否則儘量用完整單詞。
參考示例

操作類:Save()、Delete()、Update()、Print()
查詢類:Find()、Get()、Has()、Contain()
判斷類:IsValid()、IsEmpty()、HasPermission()

讀函數(獲取值):通常以 Get 開頭,如 GetName()、GetScore()。
寫函數(設置值):通常以 Set 開頭,如 SetName()、SetScore()。
布爾值相關:可用 Is/Has/Can 開頭,如 IsActive()、HasError()、CanEdit()。

實踐四: 局部變量(包括函數參數)命名遵守小駝峯規則,一般採用沒有歧義的名詞或名詞短語命名,核心思想是讓讀者無需猜測,一眼看懂變量的用途。
參考示例

int           studentCount; 
std::string   userName;

實踐五: 全局變量命名以g_ 前綴開頭,其餘遵守“實踐四”的規則。
參考示例

int           g_studentCount; 
std::string   g_userName;

實踐六: 類(結構體)成員變量命名以下劃線( _ ) 前綴開頭,其餘遵守“實踐四”的規則。
參考示例

class Student
{
public:
    SetName(const std::string& name); 
    SetAge(int age);
    
private:
    // 因為成員變量是在Student作用域內定義的,
    // 所以直接用name和agent並不會產生歧義而且還很簡單易懂。
    std::string _name;
    int           _age = 0;
};

實踐七:函數參數超過三個(為什麼三個?事不過三吧!照顧下小屏幕的道友)了就定義一個結構體來包裝一下。
參考示例

struct Student
{
    int           _age = 0;
    float         _height = 0.0;
    std::string  _name;
    std::string  _phoneNumber;
};

void Register(const Student& student)
{
    // 註冊學生信息
}

實踐八: 跨作用於傳遞內存數據的時候儘量用智能指針把管理權限也一同交出去。
參考示例

struct Student
{
    int          _age = 0;
    float        _height = 0.0;
    std::string  _name;
    std::string  _phoneNumber;
    std::string  _id;
};

std::shared_ptr<Student> Find(const std::string& id)
{
    std::shared_ptr<Student> student;
    
    if(/*找到了*/)
    {
        //  返回堆上申請的內存對象時
        //  通過共享指針同時將管理權也移交了,
        //  並且在student不再被引用時其所佔的內存會被自動釋放。
        return student;
    }
    
    return nullptr;
}

實踐九: 適當的用空行給代碼劃分下段落更有利於代碼的可讀性(一個空行就行了,多了會適得其反)。
參考示例

std::shared_ptr<Student> Find(const std::string& id)
{
    std::shared_ptr<Student> student;
    
    if(true)
    {
        //  返回堆上申請的內存對象時
        //  通過共享指針同時將管理權也移交了,
        //  並且在student不再被引用時其所佔的內存會被自動釋放。
        return student;
    }
    
    if(false)
    {
        // 執行一些異常處理或其他操作
    }
    
    return nullptr;
}

實踐十: 導入的頭文件的分組分類以段落的方式組織,看起來不會凌亂,可讀性強。
參考示例

// 導入本項目同目錄的文件
#include "server.h"
#include "configuration.h"
#include "transfer.h"

// 導入同項目其他目錄的頭文件
#include "internal/error.h"

// 導入第三方庫的頭文件
#include "core/application/core.h"

// 導入c++ 標準庫的頭文件
#include <future>

// 導入C 標準庫頭文件
#include <string.h>

説明: 按照示例的方式導入,可以儘可能的避免同一個頭文件被多次導入,且不同分組之間的頭文件以一個空行分隔可讀性很強。

實踐十一: 避免不必要的多層嵌套。
參考示例,錯誤示範(其實很常見)

// 這個代碼看起來挺離譜
// 但是確實是非常容易見到的
// 這麼些不僅結構複雜而且也增加了人的閲讀成本。
void function()
{
    if(condition1)
    {
        if(condition2)
        {
            if(condition3)
            {
                if(condition4)
                {
                    // Do something.
                 }
            }
        }
    }
}

參考示例,正確示範

// 把條件拆解,條件反寫就可以提前得出退出的
// 拆解後不僅代碼結構清晰,也非常容易被人理解。
void function()
{
    if(!condition1)
    {
        return;
    }
  
    if(!condition2)
    {
        return;
    }

    if(!condition3)
    {
        return;
    }

    if(!condition4)
    {
        return;
    }

    // Do something.
}

實踐十二: 日誌不用寫的太複雜,正確簡短的語言描述加上關鍵的參數值其實就可以了。
參考示例

// 正確示範:
// 1. 簡短且明確的信息描述:failed to xxx
// 2. 完整的關鍵參數信息:client-id, msg-id, errmsg
// 3. errmsg放到最後,因為錯誤信息可能比較長影響閲讀
Warn("failed to xxxx, msg-id: %s, client-id: %s, errmsg: %s")

// 錯誤示範:
// 1. 日誌級別錯誤
// 2. 日誌內容太少,不利於排查問題
// 3. 直接打印函數名,不安全,而且對於排查問題幾乎沒有幫助。 
// 4. 錯誤日誌內容寫了一些廢話
Info("failed to xxx")
Warn("GetHostIp")
Error("error")

就先寫到這了,其實還有很多,細節也特別多,但是如果能把上面十二條都做到,那麼寫出來的代碼也是很不錯的,後面找時間我在補充。

Add a new 評論

Some HTML is okay.