使用 c++ 模板顯示實例化解決模板函數聲明與實現(xiàn)分離的問題

開始正文之前,做一些背景鋪墊民轴,方便讀者了解我的工程需求攻柠。我的項目是一個客戶端消息分發(fā)中心,在連接上消息后臺后后裸,后臺會不定時的給我推送一些消息瑰钮,我再將它們轉發(fā)給本機的其它桌面產品去做顯示。后臺為了保證消息一定可以推到客戶端微驶,它采取了一種重復推送的策略浪谴,也就是說开睡,每次當我重新連接上后臺時,后臺會把一段時間內的消息都推給我苟耻、而不論這些消息之前是否已經推送過篇恒,如果我不加處理的直接推給產品,可能造成同一個消息重復展示多次的問題凶杖。為此胁艰,我在接收到消息后,會將它們保存在進程中的一個容器中智蝠,當有新消息到達時腾么,會先在這個容器里檢查有沒有收到這條消息,如果有杈湾,就不再轉發(fā)解虱。

1namespace GCM { 2class server_msg_t? 3? ? { 4public: 5voiddump(charconst* prompt);? 6 7std::string appname;? 8std::string uid;? 9std::string msgid; 10time_t recv_first =0; 11time_t recv_last =0; 12intrecv_cnt =0; 13? ? };1415class WorkEngine16? ? {17public:18? ? ? ? WorkEngine();19~WorkEngine();2021private:22// to avoid server push duplicate messages to same client.23// note this instance is only accessed when single connection to server arrives message, so no lock needed..24std::vector m_svrmsgs;25? ? };26}

上面的是經過簡化以后的代碼,m_svrmsgs 成員存儲的就是接收到的所有的后臺消息毛秘,server_msg_t 代表的就是一個后臺消息饭寺,appname、uid 用來定位發(fā)給哪個產品的哪個實例叫挟;msgid 用來唯一的標識一個消息艰匙;recv_first、recv_last抹恳、recv_cnt 分別表示消息接收的首次時間员凝、最后時間以及重復接收次數。那么現(xiàn)在一個很現(xiàn)實的問題就是奋献,我需要把這些消息序列化到永久存儲上去健霹,以便進程重啟后這些信息還在。這里我使用了 sqlite 數據庫瓶蚂,與此相關的代碼封裝在了 WorkEngine 的成員函數中糖埋,很容易想到的一種函數聲明方式是這樣:

1namespace GCM { 2class server_msg_t? 3? ? { 4public: 5voiddump(charconst* prompt);? 6 7std::string appname;? 8std::string uid;? 9std::string msgid; 10time_t recv_first =0; 11time_t recv_last =0; 12intrecv_cnt =0; 13? ? };1415class WorkEngine16? ? {17public:18? ? ? ? WorkEngine();19~WorkEngine();2021protected:22intdb_store_server_msg (std::vectorconst& vec); 23intdb_fetch_server_msg (std::vector & vec);2425private:26// to avoid server push duplicate messages to same client.27// note this instance is only accessed when single connection to server arrives message, so no lock needed..28std::vector m_svrmsgs;29? ? };30}31

像 line 22-23 展示的那樣,直接使用 std::vector<server_msg_t> 這個容器作為參數(有的人可能覺得我多此一舉窃这,直接在函數里訪問 m_svrmsgs 成員不就行了瞳别,為什么要通過參數傳遞呢?可能這個例子不太明顯杭攻,但是確實存在一些情況容器是作為局部變量而非成員變量存在的祟敛,這里出于說明目的做了一些簡化)。但是我覺得這樣寫太死板了兆解,萬一以后我換了容器呢馆铁,這里是不是還要改?也許是泛型算法看多了锅睛,總感覺這樣寫不夠“通用”埠巨。但是如果寫成下面這樣历谍,還是換湯不換藥:

intdb_store_server_msg (std::vector::iterator beg, std::vector::iterator end);

參考標準庫 std::copy 算法,將其改造一番辣垒,結果就成了這個樣子:

template intdb_store_server_msg(InputIterator beg, InputIterator end);

叫成員函數模板扮饶,還是成員模板函數,還是模板成員函數……說不清楚乍构,反正就是成員函數+模板函數。實現(xiàn)的話可以這樣寫:

1namespace GCM { 2template 3int WorkEngine::db_store_server_msg(InputIterator beg, InputIterator end) 4? ? { 5intret =0, rowid =0;? 6? ? ? ? qtl::sqlite::database db(SQLITE_TIMEOUT); 7 8try 9? ? ? ? {10? ? ? ? ? ? db.open(get_db_path().c_str(), NULL);11writeInfoLog("open db for store server msg OK");1213? ? ? ? ? ? db.begin_transaction();1415for(auto it = beg; it != end; ++it)16? ? ? ? ? ? {17// 1th, insert or update user info18rowid = db.insert_direct("replace into server_msg (appname, uid, msgid, first_recv, last_recv, count) values (?, ?, ?, ?, ?, ?);", 19it->appname, it->uid, it->msgid, it->recv_first, it->recv_last, it->recv_cnt);2021ret++; 22? ? ? ? ? ? }2324? ? ? ? ? ? db.commit();25? ? ? ? ? ? db.close();26writeInfoLog("replace into %d records", ret); 27? ? ? ? }28catch(qtl::sqlite::error &e)29? ? ? ? {30writeInfoLog("manipute db for store server msg error: %s", e.what());31? ? ? ? ? ? db.rollback();32? ? ? ? ? ? db.close();33return-1;34? ? ? ? }3536return ret; 37? ? }38}

可以看到扛点,核心代碼就是對迭代器區(qū)間作遍歷 (line 15)哥遮。調用方也是非常簡潔:

db_store_server_msg(m_svrmsgs.begin(), m_svrmsgs.end());

一行搞定,看起來已經大功告成了陵究,毫無難度可言眠饮,那么這篇文章想要說明什么呢?別著急铜邮,真正的難點在于從數據庫恢復數據仪召。首先直接使用迭代器是不行了,因為我們現(xiàn)在要往容器里插入元素松蒜,迭代器只能遍歷元素扔茅,一點幫助也沒有。但是相信讀者一定看過類似這樣的代碼:

正在上傳... 取消

1intmain (void) 2{ 3intarr[] = {1,3,5,7,11 };? 4? ? std::vector vec;? 5std::copy (arr, arr +sizeof(arr) /sizeof(int), std::back_inserter(vec)); 6for(auto it = vec.begin (); it != vec.end (); ++ it)? 7printf ("%d\n", *it);? 8 9return0; 10}

正在上傳... 取消

為了在容器尾部插入元素秸苗,標準庫算法借助了 back_inserter 這個東東召娜。于是自然而然的想到,我們這里能不能聲明 back_inserter 作為輸入參數呢? 例如像這樣:

template intdb_fetch_server_msg(OutputIterator it);

模板實現(xiàn)這樣寫:

正在上傳... 取消

1namespace GCM { 2template 3int WorkEngine::db_fetch_server_msg(OutputIterator it) 4? ? { 5intret =0; 6? ? ? ? qtl::sqlite::database db(SQLITE_TIMEOUT); 7 8try 9? ? ? ? {10? ? ? ? ? ? db.open(get_db_path().c_str(), NULL);11writeInfoLog("open db for fetch server msg OK");1213db.query("select appname, uid, msgid, first_recv, last_recv, count from server_msg", 14[&ret, &it](std::stringconst& appname, std::stringconst& uid, std::stringconst& msgid, time_t first_recv, time_t last_recv,int count) {15? ? ? ? ? ? ? ? ? ? server_msg_t sm; 16sm.appname = appname; 17sm.uid = uid; 18sm.msgid = msgid; 19sm.recv_first = first_recv; 20sm.recv_last = last_recv; 21sm.recv_cnt = count; 22*it = sm;23++ret; 24? ? ? ? ? ? }); 2526? ? ? ? ? ? db.close();27writeInfoLog("query %d records", ret);28? ? ? ? }29catch(qtl::sqlite::error &e)30? ? ? ? {31writeInfoLog("manipute db for store server msg error: %s", e.what());32? ? ? ? ? ? db.close();33return-1;34? ? ? ? }3536return ret;37? ? }38}

正在上傳... 取消

其實核心就是一句對 back_inserter 的賦值語句 (line 22)惊楼。調用方同樣是一行搞定:

db_fetch_server_msg (std::back_inserter(m_svrmsgs));

模板聲明與模板實現(xiàn)的分離

上面的代碼可以正常通過編譯玖瘸,但前提是模板實現(xiàn)與模板調用位于同一文件√戳考慮到這個類之前已經有許多邏輯雅倒,我決定將與數據庫相關的內容,轉移到一個新的文件(engine_db.cpp)弧可,來減少單個文件的代碼量蔑匣。調整后的文件結構如下:

+ engine.h: WorkEngine 聲明

+ engine.cpp:WorkEngine 實現(xiàn) (包含 engine.h)

+ engine_db.cpp:WorkEngine::db_xxx 模板實現(xiàn) (包含 engine.h)

重新編譯,報了一個鏈接錯誤:

1>workengine.obj : error LNK2001: 無法解析的外部符號 "protected: int __thiscall GCM::WorkEngine::db_fetch_server_msg<class std::back_insert_iterator<class std::vector<class GCM::server_msg_t,class std::allocator<class GCM::server_msg_t> > > >(class std::back_insert_iterator<class std::vector<class GCM::server_msg_t,class std::allocator<class GCM::server_msg_t> > >)" (??$db_fetch_server_msg@V?$back_insert_iterator@V?$vector@Vserver_msg_t@GCM@@V?$allocator@Vserver_msg_t@GCM@@@std@@@std@@@std@@@WorkEngine@GCM@@IAEHV?$back_insert_iterator@V?$vector@Vserver_msg_t@GCM@@V?$allocator@Vserver_msg_t@GCM@@@std@@@std@@@std@@@Z)

很明顯是模板調用時找不到對應的鏈接所致侣诺。此時需要使用“模板顯示實例化”在 engine_db.cpp 文件中強制模板生成對應的代碼實體殖演,來和 engine.cpp 中的調用點進行鏈接。需要在該文件開始處加入下面兩行代碼:

using namespace GCM;

templateintWorkEngine::db_fetch_server_msg > >(std::back_insert >);

注意模板成員函數顯示實例化的語法年鸳,我專門查了下《cpp primer》趴久,格式為:

template return_type CLASS::member_func (type1,type2, ……);

對應到上面的語句,就是使用 std::back_insert<std::vector<server_msg_t> > 代替原來的 OutputIterator 類型搔确,來告訴編譯器顯示生成這樣一個函數模板實例彼棍。注意這里相同的類型要寫兩遍灭忠,一遍是函數模板參數,一遍是函數參數座硕。然而這個顯示實例化語法卻沒有通過編譯:

1>engine_db.cpp(15): error C2061: 語法錯誤: 標識符“back_inserter”

1>engine_db.cpp(15): error C2974: 'GCM::WorkEngine::db_fetch_server_msg' : 模板 對于 'OutputIterator'是無效參數弛作,應為類型

1>? ? ? ? ? f:\gdpclient\src\gcm\gcmsvc\workengine.h(137) : 參見“GCM::WorkEngine::db_fetch_server_msg”的聲明

1>engine_db.cpp(15): error C3190: 具有所提供的模板參數的“int GCM::WorkEngine::db_fetch_server_msg(void)”不是“GCM::WorkEngine”的任何成員函數的顯式實例化

1>engine_db.cpp(15): error C2945: 顯式實例化不引用模板類專用化

百思不得其解。出去轉了一圈华匾,呼吸了一點新鮮空氣映琳,腦袋突然靈光乍現(xiàn):之前不是有一長串的鏈接錯誤嗎,把那個里面的類型直接拿來用蜘拉,應該能通過編譯萨西!說干就干,于是有了下面這一長串顯示實例化聲明:

templateintGCM::WorkEngine::db_fetch_server_msg > > >(classstd::back_insert_iterator > >)

過分的是 —— 居然通過編譯了旭旭!再仔細看看這一長串類型聲明谎脯,貌似只是把 vector 展開了而已,我用“濃縮版”的 vector 再聲明一次試下有什么變化:

templateintGCM::WorkEngine::db_fetch_server_msg > >(std::back_insert_iterator >);

居然也通過了持寄≡此螅看來只是用 back_insert_iterator 代替了 back_inserter 就好了,back_insert_iterator 又是一個什么鬼稍味?查看 back_inserter 定義废麻,有如下發(fā)現(xiàn):

1template inline back_insert_iterator<_Container>back_inserter(_Container& _Cont)2{// return a back_insert_iterator3return(_STDback_insert_iterator<_Container>(_Cont));4}

貌似 back_inserter 就是一個返回 back_insert_iterator 類型的模板函數,與 std::make_pair(a,b) 和? std::pair <A,B> 的關系很像,因為這里要的是一個類型,所以不能直接傳 back_inserter 這個函數給顯示實例化的聲明载庭。好又碌,到目前我止,我們實現(xiàn)了用一個 inserter 或兩個 iterator 參數代替笨拙的容器參數、并可以將聲明、調用、實現(xiàn)分割在三個不同的文件中社牲,已經非常完美。美中不足的是悴了,模板顯示實例化還有一些啰嗦搏恤,這里使用 typedef 定義要實例化的類型,將上面的語句改造的更清晰一些:

typedef std::back_insert_iterator > inserter_t;

template intWorkEngine::db_fetch_server_msg(inserter_t);

同理湃交,對 db_store_server_msg 進行同樣的改造:

typedef std::vector ::iterator iterator_t;

template intWorkEngine::db_store_server_msg(iterator_t, iterator_t);

這樣是不是更完美了熟空?

使用 map 代替 vector

在使用過程中,發(fā)現(xiàn)使用 map 可以更快更方便的查詢消息是否已經在容器中搞莺,于是決定將消息容器定義變更如下:

std::map m_servmsgs;

其中 map 的 value 部分與之前不變息罗,增加的 key 部分為 msgid。這樣改了之后才沧,遍歷時要使用 "it->second." 代替 "it->"迈喉;插入元素時需要使用 “*it = std::make_pair (sm.msgid, sm)” 代替 “*it = sm”绍刮。做完上述修改,我發(fā)現(xiàn)程序仍然編譯不通過挨摸。經過一番排查孩革,發(fā)現(xiàn)原來是 back_inserter 不能適配 map 容器。因為 back_inserter 對應的 back_insert_iterator 在 = 操作符中會調用容器的 push_back 接口得运,而這個接口僅有 vector膝蜈、list、deque 幾個容器支持熔掺,map 是不支持的彬檀。怎么辦呢,幸好已經有好心人寫好了 map 的插入器 —— map_inserter:

正在上傳... 取消

1#pragmaonce 2 3namespace std 4{ 5template 6class map_inserter { 7 8public: 9typedef std::map<_Key, _Value, _Compare> map_type;10? ? ? ? typedef typename map_type::value_type value_type;1112private:13map_type &m_;1415public:16map_inserter(map_type &_m)17? ? ? ? ? ? : m_(_m)18? ? ? ? {}1920public:21template22class map_inserter_helper {23public:24typedef map_inserter<_K, _V, _Cmp> mi_type;25? ? ? ? ? ? typedef typename mi_type::map_type map_type;26? ? ? ? ? ? typedef typename mi_type::value_type value_type;2728map_inserter_helper(map_type &_m)29? ? ? ? ? ? ? ? :m_(_m)30? ? ? ? ? ? {}3132constvalue_type &operator= (constvalue_type & v) {33m_[v.first] = v.second;34return v;35? ? ? ? ? ? }36private:37map_type&m_;38? ? ? ? };3940typedef map_inserter_helper<_Key, _Value, _Compare> mi_helper_type;41mi_helper_typeoperator* () {42return mi_helper_type(m_);43? ? ? ? }4445map_inserter<_Key, _Value, _Compare> &operator++() {46return*this;47? ? ? ? }4849map_inserter<_Key, _Value, _Compare> &operator++(int) {50return*this;51? ? ? ? }5253? ? };5455template56map_inserter<_K, _V, _Cmp> map_insert(std::map<_K, _V, _Cmp> &m) {57returnmap_inserter<_K, _V, _Cmp>(m);58? ? }59};

正在上傳... 取消

這段代碼我是從網上抄來的瞬女,具體請參考下面的鏈接:std::map 的 inserter 實現(xiàn)。然而不幸的是努潘,這段代碼“殘疾”了诽偷,不知道是作者盜鏈、還是沒有輸入完整的原因疯坤,這段代碼有一些先天語法缺失报慕,導致它甚至不能通過編譯,在我的不懈“腦補”過程下压怠,缺失的部分已經通過高亮部位補齊了眠冈,眾位客官可以直接享用~

特別需要說明的是,最有技術含量的缺失發(fā)生在 line 37 的一個引用符菌瘫,如果沒有加入這個蜗顽,雖然可以通過編譯,但在運行過程中雨让,inserter 不能向 map 中插入元素雇盖,會導致從數據庫讀取完成后得到空的 map。我一直嘗試查找這個文章的原文栖忠,但是一無所獲崔挖,對于互聯(lián)網傳播過程中發(fā)現(xiàn)這樣驢頭馬嘴的訛誤事件,本人表示非常痛心疾首(雖然我不是很懂庵寞,但你也不能坑我袄晗唷)……

好了,話歸正題捐川,有了 map_inserter 后脓鹃,我們就可以這樣聲明了:

typedef std::map_inserter > inserter_t;

template intWorkEngine::db_fetch_server_msg(inserter_t);

對于這個 map_inserter 實現(xiàn),我們需要傳遞 map 的三個模板參數属拾,而不是 map 本身這個參數将谊,我不太清楚是一種進步冷溶、還是一種退步,反正這個 map_inserter 有點兒怪尊浓,沒有封裝成 map_insert_iterator + map_inserter 的形式逞频,和標準庫的實現(xiàn)水平還是有差異的,大家將就看吧栋齿。調用方也需要進行一些微調:

db_fetch_server_msg(std::map_inserter >(m_svrmsgs));

看看苗胀,沒有標準庫實現(xiàn)的簡潔吧,到底是山寨貨啊~ 幸好我們已經封裝了 inserter_t 類型瓦堵,可以改寫成這樣:

db_fetch_server_msg(inserter_t(m_svrmsgs));

簡潔多了』現(xiàn)在我們再看下項目的文件組成:

1

2

3

4

5

+ map_inserter.hpp: map_inserter 聲明+實現(xiàn)

+ engine.h: WorkEngine 聲明 (包含 map_inserter.hpp)

+ engine.cpp:WorkEngine 實現(xiàn) (包含 engine.h)

+ engine_db.cpp:WorkEngine::db_xxx 模板實現(xiàn) (包含 engine.h)

……

這里為了降低復雜度,將 map_inserter 放在頭文件中進行共享菇用,類似于標準庫頭文件的使用方式澜驮。

使用普通模板函數代替類成員模板函數

本文的最后,我們再回頭看一下上面例子中的兩個成員模板函數惋鸥,發(fā)現(xiàn)它們并沒有使用到類中的其它成員杂穷,其實完全可以將它們獨立成兩個普通模板函數去調用,例如改成這樣:

正在上傳... 取消

1namespaceGCM {2classserver_msg_t3{4public:5void dump(charconst*prompt);67? ? ? ? std::stringappname;8? ? ? ? std::stringuid;9? ? ? ? std::stringmsgid;10? ? ? ? time_t recv_first =0;11? ? ? ? time_t recv_last =0;12int recv_cnt =0;13};1415classWorkEngine16{17public:18WorkEngine();19? ? ? ? ~WorkEngine();2021private:22//to avoid server push duplicate messages to same client.23//note this instance is only accessed when single connection to server arrives message, so no lock needed..24? ? ? ? std::vectorm_svrmsgs;25};2627? ? template 28intdb_store_server_msg(InputIterator beg, InputIterator end);29? ? template 30intdb_fetch_server_msg(OutputIterator it);3132? ? typedef std::map ::iterator iterator_t;33? ? typedef std::map_inserter >inserter_t;34 }

正在上傳... 取消

將模板函數聲明從類中移到類外(line 27-30)卦绣,同時修改 engine_db.cpp 中兩個類的定義和顯示實例化語句耐量,去掉類限制(WorkEngine::):

templateintdb_fetch_server_msg(inserter_t);

template intdb_store_server_msg(iterator_t, iterator_t);

調用處不需要修改。再次編譯報錯:

1>engine_db.cpp(16): warning C4667: “int GCM::db_fetch_server_msg(GCM::inserter_t)”: 未定義與強制實例化匹配的函數模板

1>engine_db.cpp(17): warning C4667: “int GCM::db_store_server_msg(GCM::iterator_t,GCM::iterator_t)”: 未定義與強制實例化匹配的函數模板

1>? ? 正在創(chuàng)建庫 F:\gdpclient\src\gcm\Release\gcmsvc.lib 和對象 F:\gdpclient\src\gcm\Release\gcmsvc.exp

1>workengine.obj : error LNK2001: 無法解析的外部符號 "int __cdecl GCM::db_fetch_server_msg<class std::map_inserter<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class GCM::server_msg_t,struct std::less<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > > >(class std::map_inserter<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class GCM::server_msg_t,struct std::less<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > >)" (??$db_fetch_server_msg@V?$map_inserter@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@Vserver_msg_t@GCM@@U?$less@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@@std@@@GCM@@YAHV?$map_inserter@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@Vserver_msg_t@GCM@@U?$less@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@@std@@@Z)

1>workengine.obj : error LNK2001: 無法解析的外部符號 "int __cdecl GCM::db_store_server_msg<class std::_Tree_iterator<class std::_Tree_val<struct std::_Tree_simple_types<struct std::pair<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const ,class GCM::server_msg_t> > > > >(class std::_Tree_iterator<class std::_Tree_val<struct std::_Tree_simple_types<struct std::pair<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const ,class GCM::server_msg_t> > > >,class std::_Tree_iterator<class std::_Tree_val<struct std::_Tree_simple_types<struct std::pair<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const ,class GCM::server_msg_t> > > >)" (??$db_store_server_msg@V?$_Tree_iterator@V?$_Tree_val@U?$_Tree_simple_types@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@Vserver_msg_t@GCM@@@std@@@std@@@std@@@std@@@GCM@@YAHV?$_Tree_iterator@V?$_Tree_val@U?$_Tree_simple_types@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@Vserver_msg_t@GCM@@@std@@@std@@@std@@@std@@0@Z)

前兩個 warning 是因為由成員函數變?yōu)槠胀ê瘮岛舐烁郏@示實例化需要放在函數實現(xiàn)后面廊蜒,我們將這兩條語句調整到文件末尾就好了。對于后面兩個鏈接 error溅漾,百思不得其解山叮,后來使用一個非常簡單的 test 模板函數做試驗,發(fā)現(xiàn)是命名空間搞的鬼添履,需要在每個函數的定義和顯示實例化語句前加上命名空間限定(GCM::):

templateintGCM::db_fetch_server_msg(inserter_t);

template intGCM::db_store_server_msg(iterator_t, iterator_t);

亞馬遜測評 www.yisuping.com

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末聘芜,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子缝龄,更是在濱河造成了極大的恐慌汰现,老刑警劉巖,帶你破解...
    沈念sama閱讀 223,002評論 6 519
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件叔壤,死亡現(xiàn)場離奇詭異瞎饲,居然都是意外死亡,警方通過查閱死者的電腦和手機炼绘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,357評論 3 400
  • 文/潘曉璐 我一進店門嗅战,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事驮捍∨蹦牛” “怎么了?”我有些...
    開封第一講書人閱讀 169,787評論 0 365
  • 文/不壞的土叔 我叫張陵东且,是天一觀的道長启具。 經常有香客問我,道長珊泳,這世上最難降的妖魔是什么鲁冯? 我笑而不...
    開封第一講書人閱讀 60,237評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮色查,結果婚禮上薯演,老公的妹妹穿的比我還像新娘。我一直安慰自己秧了,他們只是感情好跨扮,可當我...
    茶點故事閱讀 69,237評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著验毡,像睡著了一般好港。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上米罚,一...
    開封第一講書人閱讀 52,821評論 1 314
  • 那天,我揣著相機與錄音丈探,去河邊找鬼录择。 笑死,一個胖子當著我的面吹牛碗降,可吹牛的內容都是我干的隘竭。 我是一名探鬼主播,決...
    沈念sama閱讀 41,236評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼讼渊,長吁一口氣:“原來是場噩夢啊……” “哼动看!你這毒婦竟也來了?” 一聲冷哼從身側響起爪幻,我...
    開封第一講書人閱讀 40,196評論 0 277
  • 序言:老撾萬榮一對情侶失蹤菱皆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后挨稿,有當地人在樹林里發(fā)現(xiàn)了一具尸體仇轻,經...
    沈念sama閱讀 46,716評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,794評論 3 343
  • 正文 我和宋清朗相戀三年奶甘,在試婚紗的時候發(fā)現(xiàn)自己被綠了篷店。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,928評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡臭家,死狀恐怖疲陕,靈堂內的尸體忽然破棺而出方淤,到底是詐尸還是另有隱情,我是刑警寧澤蹄殃,帶...
    沈念sama閱讀 36,583評論 5 351
  • 正文 年R本政府宣布携茂,位于F島的核電站,受9級特大地震影響窃爷,放射性物質發(fā)生泄漏邑蒋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,264評論 3 336
  • 文/蒙蒙 一按厘、第九天 我趴在偏房一處隱蔽的房頂上張望医吊。 院中可真熱鬧,春花似錦逮京、人聲如沸卿堂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,755評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽草描。三九已至,卻和暖如春策严,著一層夾襖步出監(jiān)牢的瞬間穗慕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,869評論 1 274
  • 我被黑心中介騙來泰國打工妻导, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留逛绵,地道東北人。 一個月前我還...
    沈念sama閱讀 49,378評論 3 379
  • 正文 我出身青樓倔韭,卻偏偏與公主長得像术浪,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子寿酌,可洞房花燭夜當晚...
    茶點故事閱讀 45,937評論 2 361

推薦閱讀更多精彩內容