C++編程規(guī)范總結(jié)

引言

C++ 是一門十分復(fù)雜并且威力強(qiáng)大的語(yǔ)言鹿蜀,使用這門語(yǔ)言的時(shí)候我們應(yīng)該有所節(jié)制,絕對(duì)的自由意味著混亂惨好。

1.規(guī)范的作用:我十分清楚每個(gè)人對(duì)怎么編寫代碼都有自己的偏好埠巨。這里定下的規(guī)范,某些地方可能會(huì)跟個(gè)人原來(lái)熟悉的習(xí)慣相違背寓盗,并引起不滿。但多人協(xié)作的時(shí)候璧函,需要有一定規(guī)范贞让。定下一些規(guī)范,當(dāng)大家面對(duì)某些情況柳譬,有所分歧的時(shí)候喳张,容易達(dá)成共識(shí)。另外通過(guò)一定規(guī)范美澳,加強(qiáng)代碼的一致性销部,從團(tuán)隊(duì)中某人的代碼切換到另一個(gè)人的代碼,會(huì)更為自然制跟,讓別人可以讀懂你的代碼是很重要的舅桩。

2.這里規(guī)范是死的雨膨,現(xiàn)實(shí)是多變的擂涛,當(dāng)你覺(jué)得某些規(guī)范,對(duì)你需要解決問(wèn)題反而有很大限制聊记,可以違反撒妈,但要有理由恢暖,而不僅僅是借口。那到底是否在尋找借口狰右,并沒(méi)有很明確的判斷標(biāo)準(zhǔn)杰捂。這就如同不能規(guī)定少于多少根頭發(fā)為禿頭,但當(dāng)我們看到某個(gè)人的時(shí)候棋蚌,自然能夠判斷他是否是禿頭嫁佳。同樣,當(dāng)我們碰到具體情況的時(shí)候谷暮,自然能夠判斷是否在尋找借口蒿往。

3.本規(guī)范編寫過(guò)程中,大量參考了《Google C++ 編程規(guī)范》湿弦,Google那份規(guī)范十分好熄浓,建議大家對(duì)比著看。

1 格式

1.1 每行代碼不多于 80 個(gè)字符

從前的電腦終端省撑,每行只可以顯示 80 個(gè)字符《拿铮現(xiàn)在有更大更寬的顯示屏,很多人會(huì)認(rèn)為這條規(guī)則已經(jīng)沒(méi)有必要竟秫。但我們有充分的理由:

版本控制軟件娃惯,或者編碼過(guò)程中,經(jīng)常需要在同一顯示屏幕上肥败,左右并排對(duì)比新舊兩個(gè)文件趾浅。80 個(gè)字符的限制,使得兩個(gè)文件都不會(huì)折行馒稍,對(duì)比起來(lái)更清晰皿哨。

當(dāng)代碼超過(guò) 3 層嵌套,代碼行就很容易超過(guò) 80 個(gè)字符纽谒。這條規(guī)則防止我們嵌套太多層級(jí)证膨,層級(jí)嵌套太深會(huì)使得代碼難以讀懂。

規(guī)則總會(huì)有例外鼓黔。比如當(dāng)你有些代碼行央勒,是82個(gè)字符,假如我們強(qiáng)制規(guī)定少于80字符澳化,人為將一行容易讀的代碼拆分成兩行代碼崔步,就太不人性化了。

我們可以適當(dāng)超過(guò)這個(gè)限制缎谷。

1.2 使用空格(Space)井濒,而不是制表符(Tab)來(lái)縮進(jìn),每次縮進(jìn)4個(gè)字符

代碼編輯器,基本都可以設(shè)置將Tab轉(zhuǎn)為空格瑞你,請(qǐng)打開(kāi)這個(gè)設(shè)置酪惭。

制表符在每個(gè)軟件中的顯示,都會(huì)有所不同捏悬。有些軟件中每個(gè)Tab縮進(jìn)8個(gè)字符撞蚕,有些軟件每個(gè)Tab縮進(jìn)4個(gè)字符润梯,隨著個(gè)人的設(shè)置不同而不同过牙。只使用空格來(lái)縮進(jìn),保證團(tuán)隊(duì)中每個(gè)人纺铭,看同一份代碼寇钉,格式不會(huì)亂掉。

1.3 指針?lè)?hào)*舶赔,引用符號(hào)& 的位置扫倡,寫在靠近類型的地方

對(duì)比兩種寫法, 寫成第(1)種。

CCNode* p = CCNode::create(); // (1)

CCNode *p = CCNode::create(); // (2)

我知道這個(gè)規(guī)定有很大的爭(zhēng)議竟纳。指針?lè)?hào)到底靠近類型撵溃,還是靠近變量,這爭(zhēng)論一直沒(méi)有停過(guò)锥累。其實(shí)兩種寫法都沒(méi)有什么大問(wèn)題缘挑,關(guān)鍵是統(tǒng)一。經(jīng)考慮桶略,感覺(jué)第1種寫法更統(tǒng)一更合理语淘。理由:

在類中連續(xù)寫多個(gè)變量,通常會(huì)用 Tab 將變量對(duì)齊际歼。( Tab 會(huì)轉(zhuǎn)化成空格)惶翻。比如

CCNode* _a;

CCNode _b;

int _c;

當(dāng)星號(hào)靠近類型而不是變量。_a, _b, _c 等變量會(huì)很自然對(duì)齊鹅心。

而當(dāng)星號(hào)靠近變量吕粗,如果不手動(dòng)多按空格微調(diào),會(huì)寫成旭愧。

CCNode *_a;

CCNode _b;

int _c;

指針?lè)?hào)靠近類型溯泣,語(yǔ)法上更加統(tǒng)一。比如

const char* getTableName();

static_cast<CCLayer*>(node);

反對(duì)第一種寫法的理由通常是:

假如某人連續(xù)定義多個(gè)變量榕茧,就會(huì)出錯(cuò)垃沦。

int* a, b, c;

上面寫法本身就有問(wèn)題。指針應(yīng)該每行定義一個(gè)變量, 并初始化用押。

int* a = nullptr;

int* b = nullptr;

int* c = nullptr;

1.4 花括號(hào)位置

采用Allman風(fēng)格肢簿,if, for, while,namespace, 命名空間等等的花括號(hào),另起一行池充。例子

for (auto i = 0; i < 100; i++)

{

? ? printf("%d\n", i);

}

這條規(guī)定桩引,很可能又引起爭(zhēng)議。很多人采用 K&R 風(fēng)格收夸,將上面代碼寫成

for (auto i = 0; i < 100; i++) {

? ? printf("%d\n", i);

}

1.5 if, for, while等語(yǔ)句就算只有一行坑匠,也強(qiáng)制使用花括號(hào)

永遠(yuǎn)不要省略花括號(hào),不要寫成:

if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)

? ? goto fail;

需要寫成:

if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)

{

? ? goto fail;

}

省略花括號(hào)卧惜,以后修改代碼厘灼,或者代碼合并的時(shí)候,容易直接多寫一行咽瓷。如

if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)

? ? goto fail;

? ? goto fail;

就會(huì)引起錯(cuò)誤设凹。

2 命名約定

2.1 使用英文單詞,不能夾著拼音

這條規(guī)則強(qiáng)制執(zhí)行茅姜,不能有例外闪朱。

2.2 總體上采用駱駝命名法

單詞與單詞之間,使用大小寫相隔的方式分開(kāi)钻洒,中間不包含下劃線奋姿。比如

TimerManager? // (1)

playMusic? ? // (2)

其中(1)為大寫的駱駝命名法,(2)為小寫的駱駝命名法素标。

不要使用

timer_manager

play_music

這種小寫加下劃線的方式在 boost 庫(kù)称诗,C++ 標(biāo)準(zhǔn)庫(kù)中,用得很普遍糯钙。

2.3 名字不要加類型前綴

有些代碼庫(kù)粪狼,會(huì)在變量名字前面加上類型前綴。比如 b表示 bool, i 表示 int , arr 表示數(shù)組, sz 表示字符串等等任岸。他們會(huì)命名為

bool? ? ? ? ? bEmpty;

const char*? szName;

Array? ? ? ? arrTeachers;

我們不提倡這種做法再榄。變量名字應(yīng)該關(guān)注用途,而不是它的類型享潜。上面名字應(yīng)該修改為

bool? ? ? ? isEmpty;

const char* name;

Array? ? ? teachers;

注意困鸥,我們將 bool 類型添加上is。isEmpty, isOK, isDoorOpened剑按,等等疾就,讀起來(lái)就是一個(gè)詢問(wèn)句。

2.4 類型命名

類型命名采用大寫的駱駝命名法艺蝴,每個(gè)單詞以大寫字母開(kāi)頭猬腰,不包含下劃線。比如

GameObject

TextureSheet

類型的名字猜敢,應(yīng)該帶有描述性姑荷,是名詞盒延,而不要是動(dòng)詞鼠冕。盡量避開(kāi)Data, Info, Manager 這類的比較模糊的字眼懈费。(但我知道有時(shí)也真的避免不了憎乙,看著辦胶坠。)

所有的類型,class, struct, typedef, enum, 都使用相同的約定椭蹄。例如

class UrlTable

struct UrlTableProperties

typedef hash_map<UrlTableProperties*, std::string> PropertiesMap;

enum UrlTableError

2.5 變量命名

2.5.1 普通變量名字

變量名字采用小寫的駱駝命名法绳矩。比如

std::string tableName;

CCRect? ? ? shapeBounds;

變量的名字翼馆,假如作用域越長(zhǎng),就越要描述詳細(xì)中姜。作用域越短丢胚,適當(dāng)簡(jiǎn)短一點(diǎn)携龟。比如

for (auto& name : _studentNames)

{

? ? std::cout << name << std::endl;

}

for (size_t i = 0; i < arraySize; i++)

{

? ? array[i] = 1.0;

}

名字清晰峡蟋,并且盡可能簡(jiǎn)短层亿。

2.5.2 類成員變量

成員變量方灾,訪問(wèn)權(quán)限只分成兩級(jí)裕偿,private 和 public嘿棘,不要用 protected鸟妙。 私有的成員變量,前面加下劃線房午。比如:

class Image

{

public:

? ? .....

private:

? ? size_t? ? _width;

? ? size_t? ? _height;

}

public 的成員變量郭厌,通常會(huì)出現(xiàn)在 C 風(fēng)格的 struct 中,前面不用加下劃線液走。比如:

struct Color4f

{

? ? float? ? red;

? ? float? ? green;

? ? float? ? blue;

? ? float? ? alpha;

}

2.5.3 靜態(tài)變量

類中盡量不要出現(xiàn)靜態(tài)變量缘眶。類中的靜態(tài)變量不用加任何前綴。文件中的靜態(tài)變量統(tǒng)一加s_前綴顶燕,并盡可能的詳細(xì)命名涌攻。比如

static ColorTransformStack s_colorTransformStack;? ? // 對(duì)

static ColorTransformStack s_stack;? ? ? ? ? ? ? ? ? // 錯(cuò)(太簡(jiǎn)略)

2.5.4 全局變量

不要使用全局變量芝此。真的沒(méi)有辦法婚苹,加上前綴 g_,并盡可能的詳細(xì)命名廓译。比如

Document? g_currentDocument;

2.6 函數(shù)命名

變量名字采用小寫的駱駝命名法糟港。比如

playMusic

getSize

isEmpty

函數(shù)名字速和。整體上颠放,應(yīng)該是個(gè)動(dòng)詞,或者是形容詞(返回bool的函數(shù))欲低,但不要是名詞。

teacherNames();? ? ? ? // 錯(cuò)(這個(gè)是總體是名詞)

getTeacherNames();? ? // 對(duì)

無(wú)論是全局函數(shù)腊瑟,靜態(tài)函數(shù)膘格,私有的成員函數(shù),都不強(qiáng)制加前綴政敢。但有時(shí)靜態(tài)函數(shù)喷户,可以適當(dāng)加s_前綴。

類的成員函數(shù),假如類名已經(jīng)出現(xiàn)了某種信息璃谨,就不用重復(fù)寫了。比如

class UserQueue

{

public:

? ? size_t getQueueSize();? ? // 錯(cuò)(類名已經(jīng)為Queue了,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 這里再命名為getQueueSize就無(wú)意義)

? ? size_t getSize();? ? ? ? // 對(duì)

}

2.7 命名空間

命令空間的名字,使用小寫加下劃線的形式阱冶,比如

namespace lua_wrapper;

使用小寫加下劃線熙揍,而不要使用駱駝命名法届囚。可以方便跟類型名字區(qū)分開(kāi)來(lái)泥耀。比如

lua_wrapper::getField();? // getField是命令空間lua_wrapper的函數(shù)

LuaWrapper::getField();? // getField是類型LuaWrapper的靜態(tài)函數(shù)

2.8 宏命名

不建議使用宏迎瞧,但真的需要使用。宏的名字,全部大寫,中間加下劃線相連接壮吩。這樣可以讓宏更顯眼一些生百。比如

#define PI_ROUNDED 3.0

CLOVER_TEST

MAX

MIN

頭文件出現(xiàn)的防御宏定義搜吧,也全部大寫,比如:

#ifndef __COCOS2D_FLASDK_H__

#define __COCOS2D_FLASDK_H__

....

#endif

不要寫成這樣:

#ifndef __cocos2d_flashsdk_h__

#define __cocos2d_flashsdk_h__

....

#endif

2.9 枚舉命名

盡量使用 0x11 風(fēng)格 enum伺帘,例如:

enum class ColorType : uint8_t

{

? ? Black,

? ? While,

? ? Red,

}

枚舉里面的數(shù)值偶垮,全部采用大寫的駱駝命名法。使用的時(shí)候,就為 ColorType::Black

有些時(shí)候,需要使用0x11之前的enum風(fēng)格,這種情況下,每個(gè)枚舉值,都需要帶上類型信息,用下劃線分割。比如

enum HttpResult

{

? ? HttpResult_OK? ? = 0,

? ? HttpResult_Error? = 1,

? ? HttpResult_Cancel = 2,

}

2.10 純 C 風(fēng)格的接口

假如我們需要結(jié)構(gòu)里面的內(nèi)存布局精確可控,有可能需要編寫一些純C風(fēng)格的結(jié)構(gòu)和接口。這個(gè)時(shí)候,接口前面應(yīng)該帶有模塊或者結(jié)構(gòu)的名字,中間用下劃線分割。比如

struct HSBColor

{

? ? float h;

? ? float s;

? ? float b;

};

struct RGBColor

{

? ? float r;

? ? float g;

? ? float b;

}

RGBColor color_hsbToRgb(HSBColor hsb);

HSBColor color_rgbToHsb(RGBColor rgb);

這里,color 就是模塊的名字猖腕。這里的模塊,充當(dāng) C++ 中命名空間的作用。

struct Path

{

? ? ....

}

Path* Path_new();

void? Path_destrory(Path* path);

void? Path_moveTo(Path* path, float x, float y);

void? Path_lineTo(Path* path, float x, float y);

這里镜廉,接口中Path出現(xiàn)的是類的名字。

2.11 代碼文件敢茁,路徑命名

代碼文件的名字,應(yīng)該反應(yīng)出此代碼單元的作用碉哑。

比如 Point.h, Point.cpp贮尖,實(shí)現(xiàn)了class Point;

當(dāng) class Point关斜,的名字修改成裁着,Point2d, 代碼文件名字,就應(yīng)該修改成 Point2d.h, Point2d.cpp矗积。代碼文件名字,跟類型名字一樣百匆,采用大寫的駱駝命名法有巧。

路徑名字甜橱,對(duì)于于模塊的名字乘瓤。跟上一章的命名規(guī)范一樣归薛,采用小寫加下劃線的形式。比如

ui/home/HomeLayer.h

ui/battle/BattleCell.h

support/geo/Point.h

support/easy_lua/Call.h

路徑以及代碼文件名屋厘,不能出現(xiàn)空格瞻凤,中文所刀,不能夾著拼音溜族。假如隨著代碼的修改,引起模塊名乡话,類型名字的變化摧玫,應(yīng)該同時(shí)修改文件名跟路徑名。

2.12 命名避免帶有個(gè)人標(biāo)簽

比如绑青,不要將某個(gè)模塊名字為

HJCPoint

hjc/Label.h

hjc為團(tuán)隊(duì)某人名字的縮寫诬像。

項(xiàng)目歸全體成員所有,任何人都有權(quán)利跟義務(wù)整理修改工程代碼闸婴。當(dāng)某樣?xùn)|西打上個(gè)人標(biāo)記坏挠,就傾向?qū)⑵渥鳛樗接小F渌司蜁?huì)覺(jué)得那代碼亂不關(guān)自己事情邪乍,自己就不情愿別人來(lái)動(dòng)自己東西降狠。

當(dāng)然了对竣,文件開(kāi)始注釋可以出現(xiàn)創(chuàng)建者的名字,信息榜配。只是類型否纬,模塊,函數(shù)名字蛋褥,等容易在工程中散開(kāi)的東西不提倡临燃。個(gè)人項(xiàng)目可以忽略這條。

再?gòu)?qiáng)調(diào)一下烙心,任何人都有權(quán)利跟義務(wù)整理修改他人代碼膜廊,只要你覺(jué)得你修改得合理,但不要自作聰明淫茵。我知道有些程序員爪瓜,會(huì)覺(jué)得他人修改自己代碼,就是入侵自己領(lǐng)土匙瘪。

2.13 例外

有些時(shí)候铆铆,我們需要自己寫的庫(kù)跟C++的標(biāo)準(zhǔn)庫(kù)結(jié)合。這時(shí)候可以采用跟C++標(biāo)準(zhǔn)庫(kù)相類似的風(fēng)格辆苔。比如

class MyArray

{

public:

? ? typedef const char* const_iteator;

? ? ...

? ? const char* begin() const;

? ? const char* rbegin() const;

}

3 代碼文件

3.1 #define 保護(hù)

所有的頭文件算灸,都應(yīng)該使用#define來(lái)防止頭文件被重復(fù)包含。命名的格式為

__<模塊>_<文件名>_H__

很多時(shí)候驻啤,模塊名字都跟命名空間對(duì)應(yīng)菲驴。比如

#ifndef __GEO_POINT_H__

#define __GEO_POINT_H__

namespace geo

{

? ? class Point

? ? {

? ? ? ? .....

? ? };

}

#endif

并且,#define宏骑冗,的名字全部都為大寫赊瞬。不要出現(xiàn)大小寫混雜的形式。

3.2 #include 的順序

C++代碼使用#include來(lái)引入其它的模塊的頭文件贼涩。盡可能巧涧,按照模塊的穩(wěn)定性順序來(lái)排列#include的順序。按照穩(wěn)定性從高到低排列遥倦。

比如

#include <map>

#include <vector>

#include <boost/noncopyable.hpp>

#include "cocos2d.h"

#include "json.h"

#include "FlaSDK.h"

#include "support/TimeUtils.h"

#include "Test.h"

上面例子中谤绳。#include的順序,分別是C++標(biāo)準(zhǔn)庫(kù)袒哥,boost庫(kù)缩筛,第三方庫(kù),我們自己寫的跟工程無(wú)關(guān)的庫(kù)堡称,工程中比較基礎(chǔ)的庫(kù)瞎抛,應(yīng)用層面的文件。

但有一個(gè)例外却紧,就是 .cpp中桐臊,對(duì)應(yīng)的.h文件放在第一位胎撤。比如geo模塊中的, Point.h 跟 Point.cpp文件,Point.cpp中的包含

#include "geo/Point.h"

#include <cmath>

這里断凶,將 #include "geo/Point.h"伤提,放到第一位,之后按照上述原則來(lái)排列#include順序懒浮。理由下一條規(guī)范來(lái)描述飘弧。

3.3 盡可能減少頭文件的依賴

代碼文件中,每出現(xiàn)一次#include包含, 就會(huì)多一層依賴砚著。比如,有A痴昧,B類型稽穆,各自有對(duì)應(yīng)的.h文件和.cpp文件。

當(dāng)A.cpp包含了A.h, A.cpp就依賴了A.h赶撰,我們表示為

A.cpp -> A.h

這樣舌镶,當(dāng)A.h被修改的時(shí)候,A.cpp就需要重修編譯豪娜。 假設(shè)

B.cpp -> B.h

B.h? -> A.h

這表示餐胀,B.cpp 包含了B.h, B.h包含了A.h, 這個(gè)時(shí)候。B.cpp雖然沒(méi)有直接包含A.h, 但也間接依賴于A.h瘤载。當(dāng)A.h修改了否灾,B.cpp也需要重修編譯。

當(dāng)在頭文件中鸣奔,出現(xiàn)不必要的包含墨技,就會(huì)生成不必要的依賴,引起連鎖反應(yīng)挎狸,使得編譯時(shí)間大大被拉長(zhǎng)扣汪。

使用前置聲明,而不是直接#include锨匆,可以顯著地減少依賴數(shù)量崭别。實(shí)踐方法:

3.3.1 頭文件第一位包含

比如寫類A,有文件 A.h, 和A.cpp 那么在A.cpp中恐锣,將A.h的包含寫在第一位茅主。在A.cpp中寫成

// 前面沒(méi)有別的頭文件包含

#include "A.h"

#include <string>

#include .......

.... 包含其它頭文件

之后可以嘗試在 A.h 中去掉多余的頭文件。當(dāng)A.cpp可以順利編譯通過(guò)的時(shí)候侥蒙,A.h包含的頭文件就是過(guò)多或者剛剛好的暗膜。而不會(huì)是包含不夠的。

3.3.2 前置聲明

首先鞭衩,只在頭文件中使用引用或者指針学搜,而不是使用值的娃善,可以前置聲明。而不是直接包含它的頭文件瑞佩。 比如

class Test : public Base

{

public:

? ? void funA(const A& a);

? ? void funB(const B* b);

? ? void funC(const space::C& c);

private:

? ? D? _d;

};

這里聚磺,我牽涉到幾個(gè)其它類,Base, A, B, space::C(C 在命名空間space里面), D炬丸。Base和D需要知道值瘫寝,A, B, space::C只是引用和指針。所以Base, C的頭文件需要包含稠炬。A, B焕阿,space::C只需要前置聲明。

#include "Base.h"

#include "D.h"

namespace space

{

? ? class C;

}

class A;

class B;

class Test : public Base

{

public:

? ? void funA(const A& a);

? ? void funB(const B* b);

? ? void funC(const space::C& c);

private:

? ? D? _d;

};

注意命名空間里面的寫法首启。

3.3.3 impl 手法

就是類里面包含實(shí)現(xiàn)類的指針暮屡。在cpp里面實(shí)現(xiàn)。

3.3.4 盡可能將代碼拆分成相對(duì)獨(dú)立的毅桃,粒度小的單元褒纲,放到不同的文件中

簡(jiǎn)單說(shuō),就是不要將所有東西都塞在一起钥飞。這樣的代碼組積相對(duì)清晰莺掠。頭文件包含也相對(duì)較少。但現(xiàn)實(shí)中读宙,或多或少會(huì)違反彻秆。

比如,工程用到一些常量字符串(或者消息定義论悴,或者enum值掖棉,有多個(gè)變種)。一個(gè)似乎清晰的結(jié)構(gòu)膀估,是將字符串都放到同一個(gè)頭文件中幔亥。不過(guò)這樣一來(lái),這個(gè)字符串文件察纯,就幾乎會(huì)被所有項(xiàng)目文件包含帕棉。當(dāng)以后新加一個(gè)字符串時(shí)候,就算只加一行饼记,工程幾乎被全部編譯香伴。

更好的做法,是按照字符串的用途來(lái)分拆開(kāi)具则。

又比如即纲,有些支持庫(kù)。有時(shí)貪圖方便博肋,不注意的低斋,就會(huì)寫一個(gè) GlobalUtils.h 之類的頭文件蜂厅,包含所有支持庫(kù),因?yàn)檫@樣可以不關(guān)心到底應(yīng)該包含哪個(gè)膊畴,反正包含GlobalUtils.h就行掘猿,這樣多省事。不過(guò)這樣一來(lái)唇跨,需要加一個(gè)支持的函數(shù)稠通,比如就只是角度轉(zhuǎn)弧度的小函數(shù),也會(huì)發(fā)生連鎖編譯买猖。

更好的做法改橘,是根據(jù)需要來(lái)包含必要的文件。就算你麻煩一點(diǎn)政勃,寫10行#include的代碼唧龄,都比之后修改一行代碼,就編譯上10多分鐘要好奸远。

3.4 小結(jié)

減少編譯時(shí)間,這點(diǎn)很重要讽挟。再啰嗦一下

要減少頭文件重復(fù)包含懒叛,需要團(tuán)隊(duì)的人所有人達(dá)成共識(shí),認(rèn)識(shí)到這是不好的耽梅。很多人對(duì)這問(wèn)題認(rèn)識(shí)不夠薛窥,會(huì)被當(dāng)成小題大作。

不要貪方便眼姐。直接包含一個(gè)大的頭文件诅迷,短期是很方便,長(zhǎng)期會(huì)有麻煩众旗。

3.5 #include中的頭文件罢杉,盡量使用全路徑,或者相對(duì)路徑

路徑的起始點(diǎn)贡歧,為工程文件代碼文件的根目錄滩租。

比如

#include "ui/home/HomeLayer.h"

#include "ui/home/HomeCell.h"

#include "support/MathUtils.h"

不要直接包含

#include "HomeLayer.h"

#include "HomeCell.h"

#include "MathUtils.h"

這樣可以防止頭文件重名,比如一個(gè)第三方庫(kù)文件有可能就叫 MathUtils.h利朵。

并且移植到其它平臺(tái)律想,配置起來(lái)會(huì)更容易。比如上述例子绍弟,在安卓平臺(tái)上技即,就需要配置包含路徑

<Project_Root>/ui/home/

<Project_Root>/support/

也可以使用相對(duì)路徑。比如

#include "../MathUtil.h"

#include "./home/HomeCell.h"

這樣做樟遣,還有個(gè)好處而叼。就是只用一個(gè)簡(jiǎn)單腳本身笤,或者一些簡(jiǎn)單工具。就可以分析出頭文件的包含關(guān)系圖澈歉,然后就很容易看出循環(huán)依賴展鸡。

4 作用域

作用域,表示某段代碼或者數(shù)據(jù)的生效范圍埃难。作用域越大莹弊,修改代碼時(shí)候影響區(qū)域也就越大,原則上涡尘,作用域越小越好忍弛。

4.1 全局變量

禁止使用全局變量。全局變量在項(xiàng)目的任何地方都可以訪問(wèn)考抄。兩個(gè)看起來(lái)沒(méi)有關(guān)系的函數(shù)细疚,一旦訪問(wèn)了全局變量,就會(huì)產(chǎn)生無(wú)形的依賴川梅。使用全局變量疯兼,基本上都是怕麻煩,貪圖方便贫途。比如

funA -> funB -> funC -> funD

上圖表示調(diào)用順序吧彪。當(dāng)funD需要用到funA中的某個(gè)數(shù)據(jù)。正確的方式丢早,是將數(shù)據(jù)一層層往下傳遞姨裸。但因?yàn)檫@樣做,需要修改幾個(gè)地方怨酝,修改的人怕麻煩傀缩,直接定義出全局變量。這樣做农猬,當(dāng)然是可以快速fix bug赡艰。但funA跟funD就引入無(wú)形的依賴,從接口處看不出來(lái)盛险。

單件可以看做全局變量的變種瞄摊。最優(yōu)先的方式,應(yīng)該將數(shù)據(jù)從接口中傳遞苦掘,其次封裝單件换帜,再次使用函數(shù)操作靜態(tài)數(shù)據(jù),最糟糕就是使用全局變量鹤啡。

若真需要使用全局變量惯驼。變量使用g_開(kāi)頭。

4.2 類的成員變量

類的成員變量,只能夠是private或者public, 不要設(shè)置成protected祟牲。protected的數(shù)據(jù)看似安全隙畜,實(shí)際只是一種錯(cuò)覺(jué)。

數(shù)據(jù)只能通過(guò)接口來(lái)修改訪問(wèn)说贝,不要直接訪問(wèn)议惰。這樣的話,在接口中設(shè)置個(gè)斷點(diǎn)就可以調(diào)試知道什么時(shí)候數(shù)據(jù)被修改乡恕。另外改變類的內(nèi)部數(shù)據(jù)表示言询,也可以維持接口的不變,而不影響全局傲宜。

絕大多數(shù)情況运杭,數(shù)據(jù)都應(yīng)該設(shè)置成私有private, 變量加 _前綴。比如

class Data

{

private:

? ? const uint8_t*? _bytes;

? ? size_t? ? ? ? ? _size;

}

公有的數(shù)據(jù)函卒,通常出現(xiàn)在C風(fēng)格的結(jié)構(gòu)中辆憔,或者一些數(shù)據(jù)比較簡(jiǎn)單,并很常用的類报嵌,public數(shù)據(jù)不要加前綴虱咧。

class Point

{

public:

? ? Point(float x_, float y_) : x(x_), y(y_)

? ? {

? ? }

? ? .....

? ? float x;

? ? float y;

}

注意,我們?cè)跇?gòu)造函數(shù)锚国,使用 x_ 的方式表示傳入的參數(shù)彤钟,防止跟 x 來(lái)重名。

4.3 局部變量

局部變量盡可能使它的作用范圍最小跷叉。換句話說(shuō),就是需要使用的時(shí)候才定義营搅,而不要在函數(shù)開(kāi)始就全部定義云挟。

從前C語(yǔ)言有個(gè)約束,需要將用到的全部變量都定義在函數(shù)最前面转质。之后這個(gè)習(xí)慣也被傳到C++的代碼當(dāng)中园欣。但這種習(xí)慣是很不好的。

在函數(shù)最前面定義變量休蟹,變量就在整個(gè)函數(shù)都可見(jiàn)沸枯,作用域越大,就越容易被誤修改赂弓。

C++ 中绑榴,定義類型的變量,需要調(diào)用構(gòu)造函數(shù)盈魁,跟釋放函數(shù)翔怎。很多時(shí)候函數(shù)中途就退出了,這時(shí)候調(diào)用構(gòu)造函數(shù)和釋放函數(shù),就顯得浪費(fèi)赤套。

變量在最開(kāi)始的時(shí)候飘痛,很難給變量一個(gè)合理的初始值,很難的話容握,也就很容易忘記宣脉。

我們的結(jié)論是,局部變量真正需要使用的時(shí)候才定義剔氏,一行定義一個(gè)變量塑猖,并且一開(kāi)始就給它一個(gè)合適的初始值。

int i;

i = f();? ? // 錯(cuò)介蛉,初始化和定義分離

int j = g(); // 對(duì)萌庆,定義時(shí)候給出始值

4.4 命名空間

C++中,盡量不要出現(xiàn)全局函數(shù)币旧,應(yīng)該放入某個(gè)命名空間當(dāng)中践险。命名空間將全局的作用域細(xì)分,可有效防止全局作用域的名字沖突吹菱。

比如

namespace json

{

? ? class Value

? ? {

? ? ? ? ....

? ? }

}

namespace splite

{

? ? class Value

? ? {

? ? ? ? ...

? ? }

}

兩個(gè)命名空間都出現(xiàn)了Value類巍虫。外部訪問(wèn)時(shí)候,使用 json::Value, splite::Value來(lái)區(qū)分鳍刷。

4.5 文件作用域

假如占遥,某個(gè)函數(shù),或者類型输瓜,只在某個(gè).cpp中使用瓦胎,請(qǐng)將函數(shù)或者類放入匿名命名空間。來(lái)防止文件中的函數(shù)導(dǎo)出尤揣。比如

// fileA.cpp

namespace

{

? ? void doSomething()

? ? {

? ? ? ? ....

? ? }

}

上述例子搔啊,doSomething這個(gè)函數(shù),放入了匿名空間北戏。因此负芋,此函數(shù)限制在fileA.cpp中使用。另外的文件定義相同名字的函數(shù)嗜愈,也不會(huì)造成沖突旧蛾。

另外傳統(tǒng)C的做法,是在 doSomething 前面加 static, 比如

// fileB.cpp

static void doSomething()

{

? ? ...

}

doSomething也限制到文件fileB.cpp中蠕嫁。

同理锨天,只在文件中出現(xiàn)的類型,也放到匿名空間中拌阴。比如

// sqlite/Value.cpp

namespace sqlite

{

? ? namespace

? ? {

? ? ? ? class Record

? ? ? ? {

? ? ? ? ? ? ....

? ? ? ? }

? ? }

}

上述例子绍绘,匿名空間嵌套到sqlite空間中。這樣Record這個(gè)結(jié)構(gòu)只可以在sqlite/Value.cpp中使用,就算是同屬于空間sqlite的文件陪拘,也不知道 Record 的存在厂镇。

4.6 頭文件不要出現(xiàn) using namespace …

頭文件,很可能被多個(gè)文件包含左刽。當(dāng)某個(gè)頭文件出現(xiàn)了 using namespace ... 的字樣捺信,所有包含這個(gè)頭文件的文件,都簡(jiǎn)直看到此命令空間的全部?jī)?nèi)容欠痴,就有可能引起沖突迄靠。比如

// Test.h

#include <string>

using namespace std;

class Test

{

public:

? ? Test(const string& name);

};

這個(gè)時(shí)候,只要包含了Test.h, 就都看到std的所有內(nèi)容喇辽。正確的做法掌挚,是頭文件中,將命令空間寫全菩咨。將 string, 寫成 std::string, 這里不要偷懶吠式。

5 類

面向?qū)ο缶幊讨校愂腔镜拇a單元抽米。本節(jié)列舉了在寫一個(gè)類的時(shí)候特占,需要注意的事情。

5.1 讓類的接口盡可能小

設(shè)計(jì)類的接口時(shí)云茸,不要想著接口以后可能有用就先加上是目,而應(yīng)該想著接口現(xiàn)在沒(méi)有必要,就直接去掉标捺。

這里的接口懊纳,你可以當(dāng)成類的成員函數(shù)。添加接口是很容易的亡容,但是修改长踊,去掉接口會(huì)會(huì)影響較大。

接口小萍倡,不單指成員函數(shù)的數(shù)量少,也指函數(shù)的作用域盡可能小辟汰。

比如:

class Test

{

public:

? ? void funA();

? ? void funB();

? ? void funC();

? ? void funD();

};

假如列敲,funD 其實(shí)是可以使用 funA, funB, funC 來(lái)實(shí)現(xiàn)的。

這個(gè)時(shí)候帖汞,funD戴而,就不應(yīng)該放到Test里面◆嬲海可以將funD抽取出來(lái)所意。funD 只是一個(gè)封裝函數(shù),而不是最核心的。

void Test_funD(Test* test);

編寫類的函數(shù)時(shí)候扶踊,一些輔助函數(shù)泄鹏,優(yōu)先采用 Test_funD 這樣的方式,將其放到.cpp中秧耗,使用匿名空間保護(hù)起來(lái)备籽,外界就就不用知道此函數(shù)的存在,那些都只是實(shí)現(xiàn)細(xì)節(jié)分井。

當(dāng)不能抽取獨(dú)立于類的輔助函數(shù)车猬,先將函數(shù),變成private, 有必要再慢慢將其提出到public尺锚。 不要覺(jué)得這函數(shù)可能有用珠闰,一下子就寫上一堆共有接口。

再?gòu)?qiáng)調(diào)一次瘫辩,如無(wú)必要伏嗜,不要加接口。

從作用域大小杭朱,來(lái)看

獨(dú)立于類的函數(shù)阅仔,比類的成員函數(shù)要好

私有函數(shù),比共有函數(shù)要好

非虛函數(shù)弧械,比虛函數(shù)要好

5.2 聲明順序

類的成員函數(shù)或者成員變量八酒,按照使用的重要程度,從高到低來(lái)排列刃唐。

比如羞迷,使用類的時(shí)候,用戶更關(guān)注函數(shù)画饥,而不是數(shù)據(jù)衔瓮,所以成員函數(shù)應(yīng)該放到成員變量之前。 再比如抖甘,使用類的時(shí)候热鞍,用戶更關(guān)注共有函數(shù),而不是私有函數(shù)衔彻,所以public薇宠,應(yīng)該放在private前面。

具體規(guī)范

按照 public, protected, private 的順序分塊艰额。那一塊沒(méi)有澄港,就直接忽略。

每一塊中柄沮,按照下面順序排列

typedef回梧,enum废岂,struct,class 定義的嵌套類型

常量

構(gòu)造函數(shù)

析構(gòu)函數(shù)

成員函數(shù),含靜態(tài)成員函數(shù)

數(shù)據(jù)成員,含靜態(tài)數(shù)據(jù)成員

.cpp 文件中狱意,函數(shù)的實(shí)現(xiàn)盡可能給聲明次序一致湖苞。

5.3 繼承

優(yōu)先使用組合,而不是繼承髓涯。

繼承主要用于兩種場(chǎng)合:實(shí)現(xiàn)繼承袒啼,子類繼承了父類的實(shí)現(xiàn)代碼。接口繼承纬纪,子類僅僅繼承父類的方法名稱蚓再。

我們不提倡實(shí)現(xiàn)繼承,實(shí)現(xiàn)繼承的代碼分散在子類跟父親當(dāng)中包各,理解起來(lái)變得很困難摘仅。通常實(shí)現(xiàn)繼承都可以采用組合來(lái)替代。

規(guī)則:

繼承應(yīng)該都是 public

假如父類有虛函數(shù)问畅,父類的析構(gòu)函數(shù)為 virtual

假如子類覆寫了父類的虛函數(shù)娃属,應(yīng)該顯式寫上 override

比如:

// swf/Definition.h

class Definition

{

public:

? ? virtual ~Definition()? {}

? ? virtual void parse(const uint8_t* bytes, size_t len) = 0;

};

// swf/ShapeDefinition.h

class ShapeDefinition : public Definition

{

public:

? ? ShapeDefinition()? {}

? ? virtual void parse(const uint8_t* bytes, size_t len) override;

private:

? ? Shape? _shape;

};

Definition* p = new ShapeDefinition();

....

delete p;

上面的例子,使用父類的指針指向子類护姆,假如父類的析構(gòu)函數(shù)不為virtual, 就只會(huì)調(diào)用父類的Definition的釋放函數(shù)矾端,引起子類獨(dú)有的數(shù)據(jù)不能釋放。所有需要加上virtual卵皂。

另外子類覆寫的虛函數(shù)寫上秩铆,override的時(shí)候,當(dāng)父類修改了虛函數(shù)的名字灯变,就會(huì)編譯錯(cuò)誤殴玛。從而防止,父類修改了虛函數(shù)接口添祸,而忘記修改子類相應(yīng)虛函數(shù)接口的情況滚粟。

6 函數(shù)

6.1 編寫短小的函數(shù)

函數(shù)盡可能的短小,凝聚刃泌,功能單一凡壤。

只要某段代碼,可以用某句話來(lái)描述耙替,盡可能將這代碼抽取出來(lái)鲤遥,作為獨(dú)立的函數(shù),就算那代碼只有一行林艘。最典型的就是C++中的max, 實(shí)現(xiàn)只有一句話。

template <typename T>

inline T max(T a, T b)

{

? ? return a > b ? a : b;

}

將一段代碼抽取出來(lái)混坞,作為一個(gè)整體狐援,一個(gè)抽象钢坦,就不用糾結(jié)在細(xì)節(jié)之中。

將一個(gè)長(zhǎng)函數(shù)啥酱,切割成多個(gè)短小的函數(shù)爹凹。每個(gè)函數(shù)中使用的局部變量,作用域也會(huì)變小镶殷。

短小的函數(shù)禾酱,更容易復(fù)用,從一個(gè)文件搬到另一個(gè)文件也會(huì)更容易绘趋。

短小的函數(shù)颤陶,因?yàn)閮?nèi)存局部性,運(yùn)行起來(lái)通常會(huì)更快陷遮。

短小的函數(shù)滓走,也容易閱讀,調(diào)試帽馋。

6.2 函數(shù)的參數(shù)可能少搅方,原則上不超過(guò)5個(gè)

人腦短時(shí)記憶的數(shù)字是很有限的,大約可以記憶7個(gè)數(shù)字绽族。有些人多些陪腌,有些人少些。我們這里取最少值甫贯,就是5個(gè)參數(shù)篇梭。

參數(shù)的個(gè)數(shù),太多娄蔼,就很容易混亂怖喻,記不住參數(shù)的意義。

同時(shí)參數(shù)的個(gè)數(shù)太多岁诉,很可能是因?yàn)檫@個(gè)函數(shù)做的事情有點(diǎn)多了锚沸。

可以通過(guò)很多手段來(lái)減少參數(shù)的個(gè)數(shù)。比如將函數(shù)分解涕癣,分解成多個(gè)短小的函數(shù)哗蜈。或者將幾個(gè)經(jīng)常一起的參數(shù)坠韩,封裝成一個(gè)類或者結(jié)構(gòu)距潘。比如,設(shè)計(jì)一個(gè)繪畫貝塞爾曲線的接口

void drawQuadBeizer(float startX,? float startY,

? ? ? ? ? ? ? ? ? ? float controlX, float controlY,

? ? ? ? ? ? ? ? ? ? float endX,? ? float endY);

這樣的接口只搁,就不夠

void drawQuadBeizer(const Point& start,

? ? ? ? ? ? ? ? ? ? const Point& control,

? ? ? ? ? ? ? ? ? ? const Point& end);

簡(jiǎn)潔易用音比。

當(dāng)然,每個(gè)規(guī)則都會(huì)有例外氢惋。比如設(shè)置一個(gè)矩陣的數(shù)值洞翩,二維矩陣本來(lái)就需要6個(gè)數(shù)字來(lái)表示稽犁,設(shè)置接口自然需要6個(gè)參數(shù)。

6.3 函數(shù)參數(shù)順序

參數(shù)順序骚亿,按照傳入?yún)?shù)已亥,傳出參數(shù),的順序排列来屠。不要使用可傳入可傳出的參數(shù)虑椎。

bool loadFile(const std::string& filePath, ErrorCode* code);? // 對(duì)

bool loadFile(ErrorCode* code, const std::string& filePath);? // 錯(cuò)

保持統(tǒng)一的順序,使得他人容易記憶俱笛。

6.4 函數(shù)的傳出參數(shù)捆姜,使用指針,而不要使用引用

比如

bool loadFile(const std::string& filePath, ErrorCode* code);? // 對(duì)

bool loadfile(const std::string& filePath, ErrorCode& code);? // 錯(cuò)

因?yàn)楫?dāng)使用引用的時(shí)候嫂粟,使用函數(shù)的時(shí)候會(huì)變成

ErrorCode code;

if (loadFile(filePath, code))

{

? ? ...

}

而使用指針娇未,調(diào)用的時(shí)候,會(huì)是

ErrorCode code;

if (loadFile(filePath, &code))

{

? ? ...

}

這樣從星虹,&code的方式可以很明顯的區(qū)分零抬,傳入,傳出參數(shù)宽涌。試比較

doFun(arg0, arg1, arg2);? ? // 錯(cuò)

doFun(arg0, &arg1, &arg2);? // 對(duì)

6.5 不建議使用函數(shù)的缺省參數(shù)

我們經(jīng)常會(huì)通過(guò)查看現(xiàn)有的代碼來(lái)了解如何使用函數(shù)的接口平夜。缺省參數(shù)使得某些參數(shù)難以從調(diào)用方就完全清楚,需要去查看函數(shù)的接口卸亮,也就是完全了解某個(gè)接口忽妒,需要查看兩個(gè)地方。

另外兼贸,缺省參數(shù)那個(gè)數(shù)值段直,其實(shí)是實(shí)現(xiàn)的一部分,寫在頭文件是不適當(dāng)?shù)摹?/p>

缺省參數(shù)溶诞,其實(shí)可以通過(guò)將一個(gè)函數(shù)拆分成兩個(gè)函數(shù)鸯檬。實(shí)現(xiàn)放到.cpp中。

7 其它

7.1 const的使用

我們建議螺垢,盡可能的多使用const喧务。

C++中,const是個(gè)很重要的關(guān)鍵字枉圃,應(yīng)用了const之后功茴,就不可以隨便改變變量的數(shù)值了,不小心改變了編譯器會(huì)報(bào)錯(cuò)孽亲,就容易找到錯(cuò)誤的地方坎穿。只要你覺(jué)得有不變的地方,就用const來(lái)修飾吧返劲。比如:

想求圓的周長(zhǎng)玲昧,需要用到Pi, Pi不會(huì)變的犯祠,加const,const double Pi = 3.1415926;

需要在函數(shù)中傳引用酌呆,只讀,不會(huì)變的搔耕,前面加const;

函數(shù)有個(gè)返回值隙袁,返回值是個(gè)引用,只讀弃榨,不會(huì)變的菩收,前面加const;

類中有個(gè)private數(shù)據(jù),外界要以函數(shù)方式讀取鲸睛,不會(huì)變的娜饵,加const,這個(gè)時(shí)候const就是加在函數(shù)定義末尾官辈。

const的位置:

const int* name;? // 對(duì)(這樣寫箱舞,可讀性更好)

int const* name;? // 錯(cuò)

7.2 不要注釋代碼,代碼不使用就直接刪掉

有些人不習(xí)慣使用版本控制工具拳亿,某段代碼不再使用了晴股,他們會(huì)注釋掉代碼,而不是直接刪除掉肺魁。他們的理由是电湘,這段代碼現(xiàn)在沒(méi)有用,可能以后會(huì)有用鹅经,我注釋了寂呛,以后真的再用的時(shí)候,就不用再寫了瘾晃。

不要這樣做贷痪。

注釋掉的代碼,放在源文件里面酗捌,會(huì)將正常的代碼搞混亂呢诬。有個(gè)破窗理論,說(shuō)假如一個(gè)窗戶破了胖缤,不去管它尚镰,路人就會(huì)傾向敲爛其它的窗戶。同樣哪廓,假如你看到代碼某個(gè)地方亂了狗唉,會(huì)覺(jué)得再搞的更亂也沒(méi)有關(guān)系,就會(huì)越來(lái)越亂涡真。

而在現(xiàn)代的版本控制工具下分俯,只要寫好提交記錄肾筐,找回從前的代碼是很容易的。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末缸剪,一起剝皮案震驚了整個(gè)濱河市吗铐,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌杏节,老刑警劉巖唬渗,帶你破解...
    沈念sama閱讀 217,084評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異奋渔,居然都是意外死亡镊逝,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門嫉鲸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)撑蒜,“玉大人,你說(shuō)我怎么就攤上這事玄渗∽ぃ” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 163,450評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵捻爷,是天一觀的道長(zhǎng)辈灼。 經(jīng)常有香客問(wèn)我,道長(zhǎng)也榄,這世上最難降的妖魔是什么巡莹? 我笑而不...
    開(kāi)封第一講書人閱讀 58,322評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮甜紫,結(jié)果婚禮上降宅,老公的妹妹穿的比我還像新娘。我一直安慰自己囚霸,他們只是感情好腰根,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著拓型,像睡著了一般额嘿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上劣挫,一...
    開(kāi)封第一講書人閱讀 51,274評(píng)論 1 300
  • 那天册养,我揣著相機(jī)與錄音,去河邊找鬼压固。 笑死球拦,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播坎炼,決...
    沈念sama閱讀 40,126評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼愧膀,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了谣光?” 一聲冷哼從身側(cè)響起檩淋,我...
    開(kāi)封第一講書人閱讀 38,980評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎萄金,沒(méi)想到半個(gè)月后狼钮,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,414評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡捡絮,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了莲镣。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片福稳。...
    茶點(diǎn)故事閱讀 39,773評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖瑞侮,靈堂內(nèi)的尸體忽然破棺而出的圆,到底是詐尸還是另有隱情,我是刑警寧澤半火,帶...
    沈念sama閱讀 35,470評(píng)論 5 344
  • 正文 年R本政府宣布越妈,位于F島的核電站,受9級(jí)特大地震影響钮糖,放射性物質(zhì)發(fā)生泄漏梅掠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評(píng)論 3 327
  • 文/蒙蒙 一店归、第九天 我趴在偏房一處隱蔽的房頂上張望阎抒。 院中可真熱鬧,春花似錦消痛、人聲如沸且叁。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,713評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)逞带。三九已至,卻和暖如春纱新,著一層夾襖步出監(jiān)牢的瞬間展氓,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,852評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工怒炸, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留带饱,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,865評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像勺疼,于是被迫代替她去往敵國(guó)和親教寂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容