深入理解C++11 3.3 右值引用:移動(dòng)語(yǔ)義和完美轉(zhuǎn)發(fā)

首先慢洋,本章很長(zhǎng)塘雳,也較難理解陆盘,建議讀者有大段連續(xù)的時(shí)間看這個(gè)。败明。隘马。

3.3.1 指針成員與拷貝構(gòu)造

關(guān)于拷貝構(gòu)造函數(shù)的調(diào)用時(shí)間,可以看這篇文章妻顶。
如果類(lèi)中包含了指針酸员,需要小心處理蜒车,下面是一段有問(wèn)題的代碼

class C {
public:
    C():i(new int(0)){
        cout << "none argument constructor called" << endl;
    }
    
    ~C(){
       cout << "destructor called" << endl;
        delete i;
    }
    
    int* i;
};

int main(){
    C c1;
    C c2 = c1;
    
    cout << *c1.i << endl;
    cout << *c2.i << endl;
    
    return 0;
}

XCode代碼執(zhí)行輸出

none argument constructor called
0
0
destructor called
destructor called
CppTest(50956,0x1000ad5c0) malloc: *** error for object 0x10070c510: pointer being freed was not allocated
CppTest(50956,0x1000ad5c0) malloc: *** set a breakpoint in malloc_error_break to debug

原因是編譯期會(huì)默認(rèn)為類(lèi)創(chuàng)建拷貝構(gòu)造函數(shù),而默認(rèn)的拷貝構(gòu)造函數(shù)只是簡(jiǎn)單的賦值幔嗦,對(duì)類(lèi)C酿愧,系統(tǒng)默認(rèn)生成的拷貝構(gòu)造函數(shù)如

C(const C& c):i(c.i){
}

導(dǎo)致c1和c2的i值一樣,即指向同一片地址邀泉,當(dāng)c1析構(gòu)之后嬉挡,c2.i就成為了一個(gè)“懸掛指針”(dangling pointer),不再指向有效的內(nèi)存了汇恤,如果對(duì)懸掛指針再次進(jìn)行delete就會(huì)出現(xiàn)嚴(yán)重的錯(cuò)誤庞钢。
以上系統(tǒng)生成的默認(rèn)拷貝構(gòu)造函數(shù)做的是淺拷貝(shallow copy),為了解決這個(gè)問(wèn)題因谎,通常是用戶(hù)自定義拷貝構(gòu)造函數(shù)實(shí)現(xiàn)深拷貝(deep copy)基括,修正如下

class C {
public:
    C():i(new int(0)){
        cout << "none argument constructor called" << endl;
    }
    
    //增加此拷貝構(gòu)造函數(shù),根據(jù)傳入的c财岔,new一個(gè)新的int給i變量
    C(const C& c) :i(new int(*c.i)){
        
    }
    
    ~C(){
       cout << "destructor called" << endl;
        delete i;
    }
    
    int* i;
};

執(zhí)行代碼后如下

none argument constructor called
0
0
destructor called
destructor called
Program ended with exit code: 0

3.3.2 移動(dòng)語(yǔ)義

拷貝函數(shù)中為指針成員分配新的內(nèi)存再進(jìn)行內(nèi)容拷貝的方法在C++中幾乎被視為不可違背的风皿,不過(guò)有些時(shí)候卻是不必要的。如下代碼:

//這是一個(gè)成員包含指針的類(lèi)
class HasPtrMem {
public:
    HasPtrMem() : d(new int(0)) {
        cout << "Construct:" << ++n_cstr << endl;
    }
    
    HasPtrMem(const HasPtrMem& h) {
        cout << "Copy construct:" << ++n_cptr << endl;
    }
    
    ~HasPtrMem() {
        cout << "Destruct:" << ++n_dstr << endl;
    }
    
private:
    int* d;
    static int n_cstr;
    static int n_dstr;
    static int n_cptr;
};

int HasPtrMem::n_cstr = 0;
int HasPtrMem::n_dstr = 0;
int HasPtrMem::n_cptr = 0;

HasPtrMem GetTemp() {
    return HasPtrMem();//①
}

int main(){
    HasPtrMem m = GetTemp();//②
    
    return 0;
}

這里我沒(méi)用Xcode編譯運(yùn)行使鹅,因?yàn)樵贐uild Setting里增加-fno-elide-constructors編譯器依然還是優(yōu)化了揪阶,所以根據(jù)教材用命令行執(zhí)行

g++ -std=c++11 main.cpp -fno-elide-constructors

會(huì)在cpp文件下生成一個(gè)a.out文件,在命令行執(zhí)行./a.out輸出

Construct:1
Copy construct:1
Destruct:1
Copy construct:2
Destruct:2
Destruct:3

構(gòu)造函數(shù)被調(diào)用1次患朱,是在①處鲁僚,第一次調(diào)用拷貝構(gòu)造函數(shù)是在GetTemp return的時(shí)候,將①生成的變量拷貝構(gòu)造出一個(gè)臨時(shí)值裁厅,來(lái)當(dāng)做GetTemp的返回冰沙,第二次拷貝構(gòu)造函數(shù)是在②處。同時(shí)就有了于此對(duì)應(yīng)的三次析構(gòu)函數(shù)的調(diào)用执虹。例子里用的是一個(gè)int類(lèi)型的指針拓挥,而如果該指針指向的是非常大的堆內(nèi)存數(shù)據(jù)的話(huà),那沒(méi)拷貝過(guò)程就會(huì)非常耗時(shí)袋励,而且由于整個(gè)行為是透明且正確的侥啤,分析問(wèn)題時(shí)也不易察覺(jué)。

在C++中茬故,我們可以通過(guò)移動(dòng)構(gòu)造函數(shù)解決此問(wèn)題盖灸,修改代碼如下:

//這是一個(gè)成員包含指針的類(lèi)
class HasPtrMem {
public:
    HasPtrMem() : d(new int(0)) {
        cout << "Construct:" << ++n_cstr << endl;
    }
    
    HasPtrMem(const HasPtrMem& h) {
        cout << "Copy construct:" << ++n_cptr << endl;
    }
    
    HasPtrMem(HasPtrMem&& h):d(h.d) {
        h.d = nullptr; //③注意對(duì)之前的h賦空指針
        cout << "Move construct:" << ++n_mvtr << endl;
    }
    
    ~HasPtrMem() {
        cout << "Destruct:" << ++n_dstr << endl;
    }
    
private:
    int* d;
    static int n_cstr;
    static int n_dstr;
    static int n_cptr;
    static int n_mvtr;
};

int HasPtrMem::n_cstr = 0;
int HasPtrMem::n_dstr = 0;
int HasPtrMem::n_cptr = 0;
int HasPtrMem::n_mvtr = 0;

HasPtrMem GetTemp() {
    return HasPtrMem();
}

int main(){
    HasPtrMem m = GetTemp();
    
    return 0;
}

輸出

Construct:1
Move construct:1
Destruct:1
Move construct:2
Destruct:2
Destruct:3

這里通過(guò)指針賦值的方式,將d的內(nèi)存直接偷了過(guò)來(lái)磺芭,避免了拷貝構(gòu)造函數(shù)的調(diào)用赁炎。注意③,這里需要對(duì)原來(lái)的d進(jìn)行賦空值钾腺,因?yàn)樵谝苿?dòng)構(gòu)造函數(shù)完成之后徙垫,臨時(shí)對(duì)象會(huì)立即被析構(gòu)讥裤,如果不改變d,那臨時(shí)對(duì)象被析構(gòu)時(shí)姻报,因?yàn)橥祦?lái)的d和原本的d指向同一塊內(nèi)存己英,會(huì)被釋放,成為懸掛指針吴旋,會(huì)造成錯(cuò)誤剧辐。

為什么不用函數(shù)參數(shù)里帶個(gè)指針或者引用當(dāng)返回結(jié)果呢?不是性能的問(wèn)題邮府,而是代碼編寫(xiě)效率及可讀性不好荧关,如:

string *a;
int c = 1
int &b = c;
Calculate(GetTemp(),b);//最后一個(gè)參數(shù)用于返回結(jié)果

最后說(shuō)明一下移動(dòng)構(gòu)造函數(shù)被調(diào)用的時(shí)機(jī):一旦用到的是臨時(shí)變量,那么移動(dòng)語(yǔ)義就可以得到執(zhí)行褂傀。下一節(jié)講下C++的值是如何分類(lèi)的忍啤。未完待續(xù),后面還有4節(jié)仙辟。同波。。

3.3.3 左值叠国、右值與右值引用

關(guān)于左值(lvalue)和右值(rvalue)的判別方法:

  • 在賦值表達(dá)式中未檩,出現(xiàn)在等號(hào)左邊的是“左值”,等號(hào)右邊的是“右值”粟焊,如a = b + c;中冤狡,a是左值,而b+c是右值项棠;
  • 可以取地址的悲雳、有名字的是左值,反之是右值香追,對(duì)于a = b + c;合瓢,&a是允許的操作,&(b+c)是不允許的操作透典,所以a是左值晴楔,b+c是右值。

而在C++11中右值是由兩個(gè)概念構(gòu)成的峭咒,一個(gè)是將亡值(xvalue, eXpriring Value)税弃,另個(gè)一個(gè)則是純右值(prvalue, Pure Rvalue)。
其中純右值包括:

  • 非引用返回的函數(shù)返回的臨時(shí)變量值
  • 運(yùn)算表達(dá)式讹语,如1+3產(chǎn)生的臨時(shí)變量值
  • 不跟對(duì)象關(guān)聯(lián)的字面量钙皮,如2蜂科、’c‘顽决、true
  • 類(lèi)型轉(zhuǎn)換函數(shù)的返回值
  • lamda表達(dá)式

將亡值賊是C++11新增的跟右值引用相關(guān)的表達(dá)式短条,包括:

  • 返回右值引用T&&的函數(shù)返回值
  • std::move的返回值
  • 轉(zhuǎn)換為T(mén)&&的類(lèi)型轉(zhuǎn)換函數(shù)的返回值

而剩余的,可以標(biāo)識(shí)函數(shù)才菠、對(duì)象的值都屬于左值茸时。在C++11的程序中,所有的值必屬于左值赋访、將亡值可都、純右值三者之一。

在C++11中蚓耽,右值引用就是對(duì)一個(gè)右值進(jìn)行引用的類(lèi)型渠牲。由于右值不具有名字,我們也只能通過(guò)引用的方式找到它的存在步悠。通常我們只能是從右值表達(dá)式獲得其引用签杈。比如:

T&& a = ReturnRvalue();①

右值引用和左值引用都是引用類(lèi)型,都必須立即進(jìn)行初始化鼎兽。引用類(lèi)型本身并不擁有綁定對(duì)象的內(nèi)存答姥,只是該對(duì)象的一個(gè)別名。左值引用是具名變量值的別名谚咬,右值引用則是匿名變量的別名鹦付。

在上面①的例子中,ReturnRvalue函數(shù)返回的右值在表達(dá)式語(yǔ)句結(jié)束后择卦,其生命也就終結(jié)了敲长,而通過(guò)右值引用的聲明,該右值又“重獲新生”秉继,其生命期將于右值引用類(lèi)型a的生命期一樣潘明。只要a還“活著”,該右值臨時(shí)量將會(huì)一直“存活”下去秕噪。
所以相比于一下語(yǔ)句:

T b = ReturnRvalue();

①的聲明方式會(huì)少一次對(duì)象的析構(gòu)和一次對(duì)象構(gòu)造钳降。因?yàn)閍是右值引用,直接綁定了ReturnRvalue()返回的臨時(shí)量腌巾,而b是由臨時(shí)值構(gòu)造的遂填,而臨時(shí)量在表達(dá)式結(jié)束后會(huì)析構(gòu)因而會(huì)多一次析構(gòu)和構(gòu)造的開(kāi)銷(xiāo)。
注意澈蝙,能夠聲明右值引用a的前提是ReturnRvalue返回的是一個(gè)右值吓坚。通常右值引用是不能夠綁定到任何左值的,如下代碼會(huì)導(dǎo)致編譯無(wú)法通過(guò):

int c;
int &&d = c;

有的時(shí)候灯荧,我們可能不知道一個(gè)類(lèi)型是否是引用類(lèi)型礁击,以及是左值引用還是右值引用。標(biāo)準(zhǔn)庫(kù)<type_traits>頭文件中提供了3個(gè)類(lèi)模板:is_rvalue_reference、is_lvalue_reference和is_reference哆窿,比如:

cout << is_rvalue_reference<string &&>::value;

3.3.4 std::move 強(qiáng)制轉(zhuǎn)化為右值

C++11中链烈,<utility>中提供了函數(shù)std::move,功能是將一個(gè)左值強(qiáng)制轉(zhuǎn)化為右值引用挚躯,繼而我們可以通過(guò)右值引用使用該值强衡,用于移動(dòng)語(yǔ)義。std::move基本等同于一個(gè)類(lèi)型轉(zhuǎn)換:

static_cast<T&&>(lvalue);

被轉(zhuǎn)化的左值码荔,其生命期并沒(méi)有隨著左右值的轉(zhuǎn)化而改變漩勤。下面是一個(gè)正確使用std::move的例子

class HugeMem {
public:
    HugeMem(int size): sz(size>0 ? size: 1) {
        c = new int[size];
    }
    
    ~HugeMem() {
        delete [] c;
    }
    
    HugeMem(HugeMem&& h) : sz(h.sz), c(h.c) {
        h.c = nullptr;
    }
    
    int* c;
    int sz;
};

class Moveable {
public:
    Moveable(): i(new int[3]), h(1024) {}
    
    ~Moveable() {
        delete [] i;     
    }
    
    Moveable(Moveable&& m) : i(m.i), h(move(m.h)) { //使用move將m.h轉(zhuǎn)為右值引用,繼而調(diào)用HugeMem的移動(dòng)構(gòu)造函數(shù)
        m.i = nullptr;
    }
    
    int *i;
    HugeMem h;
};

Moveable getTemp() {
    Moveable tmp = Moveable();
    cout << hex << "Huge Mem from " << __func__ << "@" << tmp.h.c << endl;
    return tmp;
}

int main(){
    Moveable a(getTemp());//因?yàn)間etTemp()返回的是右值缩搅,所以會(huì)調(diào)用Moveable的移動(dòng)構(gòu)造函數(shù)
    cout << hex << "Huge Mem from " << __func__ << "@" << a.h.c << endl;
    return 0;
}

輸出

Huge Mem from getTemp@0x104002000
Huge Mem from main@0x104002000

需要注意的是越败,在編寫(xiě)移動(dòng)構(gòu)造函數(shù)的時(shí)候,應(yīng)該總是使用std::move轉(zhuǎn)換擁有形如堆內(nèi)存硼瓣、文件句柄的等資源的成員為右值眉尸,這樣一來(lái),如果成員支持移動(dòng)構(gòu)造的話(huà)巨双,就可以實(shí)現(xiàn)其移動(dòng)語(yǔ)義噪猾,即使成員沒(méi)有移動(dòng)構(gòu)造函數(shù),也會(huì)調(diào)用拷貝構(gòu)造筑累,因?yàn)椴粫?huì)引起大的問(wèn)題袱蜡。

3.3.5 移動(dòng)語(yǔ)義的一些其他問(wèn)題

移動(dòng)語(yǔ)義一定是要改變臨時(shí)變量的值(這里有以為,需要解決慢宗,目前沒(méi)看出哪里一定要改變坪蚁,先這么硬背吧)。如聲明:

Moveable(const Moveale &&);//這個(gè)對(duì)應(yīng)3.3.4的例子镜沽,如果這樣聲明移動(dòng)構(gòu)造函數(shù)會(huì)報(bào)錯(cuò)
image.png

而如果是將3.3.4的例子中的Moveable getTemp()改為const Moveable getTemp(),再執(zhí)行命令

g++ -std=c++11 main.cpp -fno-elide-constructors

注意上面的改動(dòng)在Xcode中是可以運(yùn)行的敏晤,可以正確調(diào)用到移動(dòng)構(gòu)造函數(shù),但是通過(guò)命令行會(huì)提示

copy constructor is implicitly deleted because 'Moveable' has a user-declared move constructor

可見(jiàn)Moveable a(getTemp());實(shí)際是要調(diào)用Moveable的拷貝構(gòu)造函數(shù)缅茉。報(bào)錯(cuò)原因顯示聲明了移動(dòng)構(gòu)造函數(shù)嘴脾,編譯器就不會(huì)為類(lèi)生成默認(rèn)的拷貝構(gòu)造函數(shù)了,所以提示沒(méi)有顯示聲明拷貝構(gòu)造函數(shù)蔬墩。

在C++11中译打,拷貝/移動(dòng)改造函數(shù)有以下3個(gè)版本:

  • T Object(T&)
  • T Object(const T&)
  • T Object(T&&)

其中常量左值引用的版本是一個(gè)拷貝構(gòu)造函數(shù)版本,右值引用參數(shù)的是一個(gè)移動(dòng)構(gòu)造函數(shù)版本拇颅。默認(rèn)情況下奏司,編譯器會(huì)為程序員隱式地生成一個(gè)移動(dòng)構(gòu)造函數(shù),但是如果聲明了一自定義的拷貝構(gòu)造函數(shù)樟插、拷貝賦值函數(shù)韵洋、移動(dòng)構(gòu)造函數(shù)竿刁、析構(gòu)函數(shù)中的一個(gè)或者多個(gè),編譯器都不會(huì)再生成默認(rèn)版本搪缨。所以在C++11中食拜,拷貝構(gòu)造函數(shù)、拷貝賦值函數(shù)勉吻、移動(dòng)構(gòu)造函數(shù)和移動(dòng)賦值函數(shù)必須同時(shí)提供,或者同時(shí)不提供旅赢,只聲明其中一種的話(huà)齿桃,類(lèi)都僅能實(shí)現(xiàn)一種語(yǔ)義。

只實(shí)現(xiàn)一種語(yǔ)義在類(lèi)的編寫(xiě)中也是非常常見(jiàn)的煮盼,比如如果只實(shí)現(xiàn)移動(dòng)語(yǔ)義短纵,則表明該類(lèi)型的變量擁有的資源只能被移動(dòng),不能被拷貝僵控,那么這樣的資源必須是唯一的香到,如智能指針、文件流报破。

在<type_traits>里悠就,可以通過(guò)一些輔助的模板類(lèi)來(lái)判斷一個(gè)類(lèi)型是否是可以移動(dòng)的,如:

  • is_move_constructible
  • is_trivially_move_constructible
  • is_nothrow_move_constructible

使用方法都是使用value成員充易,如

cout << is_move_constructible<UnknowTYpe>::value;

有了移動(dòng)語(yǔ)義梗脾,可以實(shí)現(xiàn)高性能的置換函數(shù),如:

template <class T>
void swap(T& a, T& b) {
    T tmp(move(a));
    a = move(b);
    b = move(tmp);
}

如果T是可以移動(dòng)的盹靴,則不會(huì)有資源的釋放和申請(qǐng)炸茧,如果T不可移動(dòng)但是可以拷貝,則和普通聲明一樣了稿静。

要注意的是梭冠,盡量不要編寫(xiě)會(huì)拋出異常的移動(dòng)構(gòu)造函數(shù),因?yàn)橛锌赡芤苿?dòng)沒(méi)完成改备,會(huì)導(dǎo)致一些指針成為懸掛指針控漠,通過(guò)添加noexcept關(guān)鍵字,可以保證移動(dòng)構(gòu)造函數(shù)拋出異常直接終止程序悬钳。

3.3.6 完美轉(zhuǎn)發(fā)

完美轉(zhuǎn)發(fā)(perfect forwarding)润脸,是指在模板函數(shù)中,完全依照模板的參數(shù)類(lèi)型講參數(shù)傳遞給模板中調(diào)用的另外一個(gè)函數(shù)他去,如:

template <typename T>
void IamForwarding(T t) {
    IrunCodeActually(t);
}

這是一個(gè)參數(shù)透?jìng)鞯膶?shí)現(xiàn)毙驯,但是因?yàn)槭褂米罨绢?lèi)型轉(zhuǎn)發(fā),會(huì)在傳參的時(shí)候產(chǎn)生一次額外的臨時(shí)對(duì)象拷貝灾测,因?yàn)橹荒苷f(shuō)是轉(zhuǎn)發(fā)爆价,但不完美垦巴。所以通常需要的是一個(gè)引用類(lèi)型餐護(hù)士,不會(huì)有拷貝的開(kāi)銷(xiāo)铭段。其次需要考慮函數(shù)對(duì)類(lèi)型的接受能力骤宣,因?yàn)槟繕?biāo)函數(shù)可能需要既接受左值引用,又接受右值引用序愚,如果轉(zhuǎn)發(fā)函數(shù)只能接受其中的一部分憔披,也不完美。

對(duì)應(yīng)代碼

typedef const A T;
typedef T& TR;
TR& v = 1;

在C++11中引入了一條所謂“引用折疊”的新語(yǔ)言規(guī)則爸吮,規(guī)則如下

TR的類(lèi)型定義 聲明v的類(lèi)型 v的實(shí)際類(lèi)型
T& TR A&
T& TR& A&
T& TR&& A&
T&& TR A&&
T&& TR& A&
T&& TR&& A&&

規(guī)則就是一單定義中出現(xiàn)了左值引用芬膝,引用折疊總是優(yōu)先將其折疊為左值引用。前三行TR定義為T(mén)&形娇,則v世界類(lèi)型為A&锰霜,第五行的v的類(lèi)型為T(mén)R&,則v的實(shí)際類(lèi)型也為A&桐早,其他則為右值引用癣缅。于是我們把轉(zhuǎn)發(fā)函數(shù)改為:

template <typename T>
void IamForwarding(T&& t) {
    IrunCodeActually(static_cast<T&&>(t));
}

對(duì)于傳入的左值引用

void IamForwarding(X& && t) {
    IrunCodeActually(static_cast<X& &&>(t));
}

折疊后是

void IamForwarding(X& t) {
    IrunCodeActually(static_cast<X&>(t));
}

對(duì)于右值引用

void IamForwarding(X&& && t) {
    IrunCodeActually(static_cast<X&& &&>(t));
}

折疊后是

void IamForwarding(X&& t) {
    IrunCodeActually(static_cast<X&&>(t));
}

此處的static_cast類(lèi)似std::move的作用,將左值轉(zhuǎn)換為右值引用哄酝。不過(guò)在C++11中友存,用于完美轉(zhuǎn)發(fā)的函數(shù)不叫move,叫forward陶衅,所以也可以這么寫(xiě)

void IamForwarding(X&& t) {
    IrunCodeActually(forward(t));
}

move和forward實(shí)現(xiàn)差別不大爬立,但是為了不同用途,有了不同命名万哪。
下面是完美轉(zhuǎn)發(fā)的例子:

void run(int && m) { cout << "rvalue ref" << endl; }
void run(int & m) { cout << "lvalue ref" << endl; }
void run(const int && m) { cout << "const rvalue ref" << endl; }
void run(const int & m) { cout << "const lvalue ref" << endl; }

template <typename T>
void perfectForward(T&& t) {
    run(forward<T>(t));
}

int main(){
    int a;
    int b;
    const int c = 1;
    const int d = 0;
    
    perfectForward(a);
    perfectForward(move(b));
    perfectForward(c);
    perfectForward(move(d));
    
    return 0;
}

輸出

lvalue ref
rvalue ref
const lvalue ref
const rvalue ref
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末侠驯,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子奕巍,更是在濱河造成了極大的恐慌吟策,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件的止,死亡現(xiàn)場(chǎng)離奇詭異檩坚,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)诅福,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén)匾委,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人氓润,你說(shuō)我怎么就攤上這事赂乐。” “怎么了咖气?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵挨措,是天一觀的道長(zhǎng)挖滤。 經(jīng)常有香客問(wèn)我,道長(zhǎng)浅役,這世上最難降的妖魔是什么斩松? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮觉既,結(jié)果婚禮上惧盹,老公的妹妹穿的比我還像新娘。我一直安慰自己瞪讼,他們只是感情好钧椰,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著尝艘,像睡著了一般演侯。 火紅的嫁衣襯著肌膚如雪姿染。 梳的紋絲不亂的頭發(fā)上背亥,一...
    開(kāi)封第一講書(shū)人閱讀 51,292評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音悬赏,去河邊找鬼狡汉。 笑死,一個(gè)胖子當(dāng)著我的面吹牛闽颇,可吹牛的內(nèi)容都是我干的盾戴。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼兵多,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼尖啡!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起剩膘,我...
    開(kāi)封第一講書(shū)人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤衅斩,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后怠褐,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體畏梆,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年奈懒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了奠涌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡磷杏,死狀恐怖溜畅,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情极祸,我是刑警寧澤达皿,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布天吓,位于F島的核電站,受9級(jí)特大地震影響峦椰,放射性物質(zhì)發(fā)生泄漏龄寞。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一汤功、第九天 我趴在偏房一處隱蔽的房頂上張望物邑。 院中可真熱鬧,春花似錦滔金、人聲如沸色解。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)科阎。三九已至,卻和暖如春忿族,著一層夾襖步出監(jiān)牢的瞬間锣笨,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工道批, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留错英,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓隆豹,卻偏偏與公主長(zhǎng)得像椭岩,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子璃赡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

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