C++11——拷貝控制

對拷貝控制成員使用= default

我們可以通過將拷貝控制成員定義為= default践叠,顯示地要求編譯器生成它們的合成版本:

Code:
    class Sales_data {
    public:
        // copy control; use defaults
        Sales_data() = default;
        Sales_data(const Sales_data&) = default;
        Sales_data& operator=(const Sales_data &);
        ~Sales_data() = default;
        // other members as before
    };
    Sales_data& Sales_data::operator=(const Sales_data&) = default;

當(dāng)我們在類體內(nèi)的成員聲明中指定= default時(shí)禁灼,編譯器生成的合成函數(shù)是隱式內(nèi)聯(lián)的(就像在類體中定義的任何其他成員函數(shù)一樣)轿曙。如果我們不希望合成函數(shù)是內(nèi)聯(lián)函數(shù)导帝,我們可以在該函數(shù)的定義上指定= default,就像在重載=運(yùn)算符的定義中那樣斋荞。
注:我們只能對具有合成版本的成員函數(shù)使用= default(即默認(rèn)構(gòu)造函數(shù)或拷貝控制成員)平酿。

使用= delete來阻止拷貝類對象

在新標(biāo)準(zhǔn)下悦陋,我們可以通過將拷貝構(gòu)造函數(shù)和賦值運(yùn)算符定義為已刪除函數(shù)來阻止復(fù)制叨恨。已刪除的函數(shù)是已聲明的函數(shù),但不能以任何其他方式使用秉颗。我們使用= delete跟隨想要?jiǎng)h除的函數(shù)的參數(shù)列表來將函數(shù)定義為已刪除:

Code:
    struct NoCopy {
        NoCopy() = default;      // use the synthesized default constructor
        NoCopy(const NoCopy&) = delete;            // no copy
        NoCopy &operator=(const NoCopy&) = delete; // no assignment
        ~NoCopy() = default;    // use the synthesized destructor
        // other members
    }

= delete關(guān)鍵字即告訴編譯器又告訴代碼閱讀者蚕甥,我們故意沒有定義這些成員栋荸。
= default不同,= delete必須出現(xiàn)在已刪除函數(shù)的第一個(gè)聲明中帅霜。從邏輯上講呼伸,這種差異源自這些聲明的含義。默認(rèn)成員只影響編譯器生成的代碼搂根;因此在編譯器生成代碼之前不需要= default铃辖。另一方面娇斩,編譯器需要知道一個(gè)函數(shù)被刪除,以禁止試圖使用它的操作五督。
= default不同充包,我們可以在任何函數(shù)上指定= delete(我們可以在默認(rèn)構(gòu)造函數(shù)或編譯器可以合成的拷貝控件成員上使用= default)遥椿。雖然刪除函數(shù)的主要用途是抑制拷貝控制成員冠场,但是當(dāng)我們想要引導(dǎo)函數(shù)匹配過程時(shí),刪除函數(shù)有時(shí)也很有用钢悲。

移動(dòng)而不是復(fù)制類對象

我們可以使用新標(biāo)準(zhǔn)庫引入的兩個(gè)工具來避免復(fù)制字符串莺琳。首先载慈,包括字符串在內(nèi)的幾個(gè)庫類定義了所謂的“移動(dòng)構(gòu)造函數(shù)”办铡。字符串移動(dòng)構(gòu)造函數(shù)如何工作的詳細(xì)信息與其他任何有關(guān)實(shí)現(xiàn)的詳細(xì)信息都沒有公開琳要。但是稚补,我們知道移動(dòng)構(gòu)造函數(shù)通常通過將資源從給定對象“移動(dòng)”到正在構(gòu)造的對象來操作嫂伞。我們也知道庫保證“移動(dòng)”字符串保持有效帖努,可破壞的狀態(tài)粪般。對于字符串亩歹,我們可以想象每個(gè)字符串都有一個(gè)指向char數(shù)組的指針。據(jù)推測亭姥,字符串移動(dòng)構(gòu)造函數(shù)復(fù)制指針而不是為字符本身分配空間和復(fù)制字符达罗。
我們將使用的第二個(gè)工具是名為move的庫函數(shù)静秆,它在頭文件<utility>中定義(關(guān)于utility更多的信息可參考header <utility>)抚笔。目前殊橙,關(guān)于move有兩點(diǎn)需要了解。首先螃概,當(dāng)reallocate在新內(nèi)存中構(gòu)造字符串時(shí)鸽疾,它必須調(diào)用move來表示它想要使用字符串移動(dòng)構(gòu)造函數(shù)制肮。如果省略了調(diào)用move來移動(dòng)字符串,則將使用拷貝構(gòu)造函數(shù)综液。其次谬莹,我們通常不提供move的使用說明附帽。當(dāng)我們使用move時(shí),我們調(diào)用std::move整胃,而不是move喳钟。關(guān)于move的更多的信息可參考std::move奔则。

右值引用

為了支持move操作,新標(biāo)準(zhǔn)引入了一種新的引用酬蹋,即右值應(yīng)用除嘹。右值引用是必須綁定到右值的引用岸蜗。通過使用&&而不是&獲得右值引用璃岳。正如我們將看到的铃慷,右值引用具有重要的屬性,它們可能只綁定到即將被銷毀的對象上洲鸠。因此,我們可以自由地將資源從一個(gè)右值引用“移動(dòng)”到另一個(gè)對象。
左值和右值是表達(dá)式的屬性扒腕。有些表達(dá)式產(chǎn)生或需要左值绢淀;另一些表達(dá)式產(chǎn)生或需要右值。一般來說瘾腰,左值表達(dá)式指的是對象的標(biāo)識皆的,而右值表達(dá)式指的是對象的值。
與任何引用一樣蹋盆,右值引用只是對象的另一個(gè)名稱费薄。我們知道栖雾,當(dāng)我們需要將它們與右值引用區(qū)分開時(shí)楞抡,我們將其稱為左值引用,我們不能將常規(guī)引用綁定到需要轉(zhuǎn)換的表達(dá)式岩灭,文本或返回右值的表達(dá)式拌倍。右值引用具有相反的綁定屬性:我們可以將右值引用綁定到這些類型的表達(dá)式赂鲤,但是我們不能直接將右值引用綁定到左值:

Code:
    int i = 42;
    int &r = i;      // ok: r refers to i
    int &&rr = i;    // error: cannot bind an rvalue reference to an lvalue
    int &r2 = i * 42;    // error: i * 42 is an rvalue
    const int &r3 = i * 42; // ok: we can bind a reference to const to an rvalue
    int &&rr2 = i * 42;  // ok: bind rr2 to the result of the multiplication

返回左值引用的函數(shù)以及賦值噪径,下標(biāo),解引用和前綴增量/減量運(yùn)算符都是返回左值的表達(dá)式的示例数初。我們可以將左值引用綁定到任何這些表達(dá)式的結(jié)果找爱。
返回非引用類型的函數(shù),以及算術(shù)泡孩,關(guān)系车摄,按位和后綴增量/減量運(yùn)算符,都產(chǎn)生右值仑鸥。我們不能將左值引用綁定到這些表達(dá)式吮播,但我們可以綁定對const的左值引用或?qū)@些表達(dá)式的右值引用。

庫函數(shù)move

雖然我們不能直接將右值引用綁定到左值眼俊,但我們可以顯式地將左值轉(zhuǎn)換為其對應(yīng)的右值引用類型意狠。我們還可以通過調(diào)用名為move的新標(biāo)準(zhǔn)庫函數(shù)來獲取綁定到左值的右值引用,該函數(shù)在utility頭文件中定義疮胖。move函數(shù)的定義如下:

Code:
    template <typename T>
    typename remove_reference<T>::type&& move(T&& t)
    {
        return static_cast<typename remove_reference<T>::type&&>(t);
    }

我們可以以如下方式使用move函數(shù);

Code:
    int &&rr3 = std::move(rr1);  // ok

調(diào)用move告訴編譯器我們有一個(gè)左值环戈,我們要將它視為右值。必須認(rèn)識到move承諾澎灸,我們不打算再次使用rr1院塞,除非對它賦值或銷毀它。在調(diào)用move之后性昭,我們無法對被操作對象的值做出任何假設(shè)拦止。
注:我們可以銷毀一個(gè)被move操作的對象,也可以為它分配一個(gè)新值糜颠,但是我們不能使用它的值汹族。
注:使用move的代碼應(yīng)該使用std::move艺玲,而不是move。這樣做可以避免潛在的名稱沖突鞠抑。

移動(dòng)構(gòu)造和移動(dòng)賦值

我們可以在自己的類中定義移動(dòng)構(gòu)造和移動(dòng)賦值函數(shù)饭聚,這種函數(shù)與拷貝構(gòu)造函數(shù)類型,但它們從給定對象中“竊取資源”而不是復(fù)制資源搁拙。
與拷貝構(gòu)造函數(shù)一樣秒梳,移動(dòng)構(gòu)造函數(shù)具有一個(gè)初始參數(shù),該參數(shù)是對類類型的引用箕速。與拷貝構(gòu)造函數(shù)不同酪碘,移動(dòng)構(gòu)造函數(shù)中的引用參數(shù)是右值引用。與拷貝構(gòu)造函數(shù)一樣盐茎,任何其他參數(shù)都必須具有默認(rèn)參數(shù)兴垦。
除了移動(dòng)資源之外,移動(dòng)構(gòu)造函數(shù)還必須確保移動(dòng)的對象處于一種合法狀態(tài)字柠,以便無影響地銷毀該對象探越。特別是,一旦移動(dòng)其資源窑业,原始對象就不能再指向那些被移動(dòng)的資源钦幔,而新創(chuàng)建的對象負(fù)責(zé)這些被移動(dòng)的資源。
作為一個(gè)例子常柄,我們將定義StrVec移動(dòng)構(gòu)造函數(shù)來移動(dòng)而不是將元素從一個(gè)StrVec復(fù)制到另一個(gè):

Code:
/***********************
    class StrVec{
    private:
        std::string *elements;  // pointer to the first element in the array
        std::string *first_free; // pointer to the first free element in the array
        std::string *cap;  // pointer to one past the end of the array
    };
***********************/
    StrVec::StrVec(StrVec &&s) noexcept  // move won't throw any exceptions
         // member initializers take over the resources in s
      : elements(s.elements), first_free(s.first_free), cap(s.cap)
    {
        // leave s in a state in which it is safe to run the destructor
        s.elements = s.first_free = s.cap = nullptr;
    }

與復(fù)制構(gòu)造函數(shù)不同鲤氢,移動(dòng)構(gòu)造函數(shù)不分配任何新內(nèi)存;它接管給定StrVec中的內(nèi)存西潘。從其參數(shù)接管內(nèi)存后卷玉,構(gòu)造函數(shù)體將給定對象中的指針設(shè)置為nullptr。 移動(dòng)對象后喷市,該對象繼續(xù)存在相种。最終,移動(dòng)的對象將被銷毀东抹,這意味該對象將運(yùn)行析構(gòu)函數(shù)蚂子。StrVec析構(gòu)函數(shù)在first_free上調(diào)用deallocate。如果我們忽略了改變s.first_free缭黔,那么銷毀給定對象將刪除我們剛剛移動(dòng)的資源食茎。

移動(dòng)構(gòu)造函數(shù)通常應(yīng)該是noexcept

因?yàn)橐苿?dòng)操作通過“竊取”資源來執(zhí)行,所以它通常不會自己分配任何資源馏谨。 因此别渔,移動(dòng)操作通常不會引發(fā)任何異常。當(dāng)我們編寫一個(gè)不能拋出異常的移動(dòng)操作時(shí),我們應(yīng)該告知庫這個(gè)事實(shí)哎媚。正如我們所看到的喇伯,除非庫知道移動(dòng)構(gòu)造函數(shù)不會拋出異常,否則它將做額外的工作拨与,以滿足移動(dòng)類類型的對象可能拋出異常的可能性稻据。
通知庫的一種方法是在構(gòu)造函數(shù)上指定noexceptnoexcept是我們承諾函數(shù)不會拋出任何異常的一種方式买喧。我們在函數(shù)的參數(shù)列表之后指定noexcept捻悯。在構(gòu)造函數(shù)中,noexcept出現(xiàn)在參數(shù)列表和以開始的成員初始化列表之間:

Code:
    class StrVec {
    public:
        StrVec(StrVec&&) noexcept;  // move constructor
          // other members as before
    };
    StrVec::StrVec(StrVec &&s) noexcept : // member initializers
    { /* constructor body  */ }

如果函數(shù)的定義出現(xiàn)在類之外淤毛,我們必須在類頭中的聲明和定義上都指定noexcept今缚。
理解為什么需要noexcept可以幫助加深我們對庫如何與我們編寫的類型的對象交互的理解。我們需要指出一個(gè)移動(dòng)操作不會拋出異常低淡,因?yàn)橛袃蓚€(gè)相互關(guān)聯(lián)的事實(shí):第一姓言,盡管移動(dòng)操作通常不拋出異常,但允許它們這樣做蔗蹋。第二何荚,庫容器提供了在發(fā)生異常時(shí)所做操作的保證。例如纸颜,vector保證如果在我們調(diào)用push_back時(shí)發(fā)生異常兽泣,那么向量本身將保持不變绎橘。
現(xiàn)在讓我們考慮一下push_back內(nèi)部會發(fā)生什么胁孙。與相應(yīng)的StrVec操作一樣,vector上的push_back可能需要重新分配向量称鳞。當(dāng)重新分配向量時(shí)涮较,它會將元素從其舊空間移動(dòng)到新內(nèi)存。
正如我們之前所見冈止,移動(dòng)一個(gè)對象通常會更改被移動(dòng)的對象的值狂票。如果重新分配使用移動(dòng)構(gòu)造函數(shù),并且該構(gòu)造函數(shù)在移動(dòng)一些但不是所有元素后拋出異常熙暴,則會出現(xiàn)問題闺属。舊空間中的已經(jīng)移動(dòng)的元素將被更改,新空間中未構(gòu)造的元素將不存在周霉。在這種情況下掂器,vector將無法滿足其保持向量不變的要求。
另一方面俱箱,如果vector使用復(fù)制構(gòu)造函數(shù)并發(fā)生異常国瓮,則可以輕松滿足此要求。在這種情況下,當(dāng)元素在新內(nèi)存中構(gòu)造時(shí)乃摹,舊元素保持不變禁漓。如果發(fā)生異常,vector可以釋放它已經(jīng)分配的但無法成功構(gòu)造的空間并返回孵睬。原始的vector元素仍然存在播歼。
為了避免這個(gè)潛在的問題,vector在重新分配期間必須使用拷貝構(gòu)造函數(shù)而不是移動(dòng)構(gòu)造函數(shù)掰读,除非它知道元素類型的移動(dòng)構(gòu)造函數(shù)不能拋出異常荚恶。如果我們希望移動(dòng)我們類型的對象而不是在vector重新分配等情況下復(fù)制,我們必須明確告訴庫我們的移動(dòng)構(gòu)造函數(shù)是安全的磷支。我們通過標(biāo)記移動(dòng)構(gòu)造函數(shù)(和移動(dòng)賦值運(yùn)算符)noexcept來實(shí)現(xiàn)谒撼。

移動(dòng)迭代器

我們在重新分配成員時(shí)可以使用for循環(huán)來調(diào)用構(gòu)造函數(shù)以將元素從舊內(nèi)存復(fù)制到新內(nèi)存。作為編寫該循環(huán)的替代方法雾狈,如果我們可以調(diào)用uninitialized_copy來構(gòu)造新分配的空間廓潜,那將會更容易。但是uninitialized_copy只做復(fù)制工作善榛。沒有類似的庫函數(shù)可以將對象“移動(dòng)”到未構(gòu)造的內(nèi)存中辩蛋。
相反,新標(biāo)準(zhǔn)庫定義了一個(gè)移動(dòng)迭代器適配器移盆。移動(dòng)迭代器通過更改迭代器的解引用運(yùn)算符的行為來調(diào)整其給定的迭代器悼院。通常,迭代器解引用運(yùn)算符返回對該元素的左值引用咒循。與其他迭代器不同据途,移動(dòng)迭代器的取消引用運(yùn)算符產(chǎn)生右值引用。
我們通過調(diào)用庫make_move_iterator函數(shù)將普通迭代器轉(zhuǎn)換為移動(dòng)迭代器叙甸。此函數(shù)接收迭代器并返回移動(dòng)迭代器颖医。
返回的移動(dòng)迭代器的所有操作都和原始迭代器一樣。因?yàn)檫@些迭代器支持常規(guī)迭代器操作裆蒸,所以我們可以將一對移動(dòng)迭代器傳遞給算法熔萧。特別是,我們可以將移動(dòng)迭代器傳遞給uninitialized_copy

Code:
    void StrVec::reallocate()
    {
        // allocate space for twice as many elements as the current size
        auto newcapacity = size() ? 2 * size() : 1;
        auto first = alloc.allocate(newcapacity);
        // move the elements
        auto last = uninitialized_copy(make_move_iterator(begin()),
                                  make_move_iterator(end()),
                                  first);
        free();            // free the old space
        elements = first;  // update the pointers
        first_free = last;
        cap = elements + newcapacity;
    }

uninitialized_copy調(diào)用輸入序列中每個(gè)元素的構(gòu)造函數(shù)僚祷,以將該元素“復(fù)制”到目標(biāo)中佛致。該算法使用迭代器解引用運(yùn)算符從輸入序列中獲取元素。因?yàn)槲覀儌鬟f了移動(dòng)迭代器辙谜,所以解引用運(yùn)算符產(chǎn)生一個(gè)右值引用俺榆,這意味著構(gòu)造將使用移動(dòng)構(gòu)造函數(shù)來構(gòu)造元素。
值得注意的是筷弦,標(biāo)準(zhǔn)庫不保證哪些算法可以與移動(dòng)迭代器一起使用肋演,哪些算法不能抑诸。因?yàn)橐苿?dòng)對象可以消除源對象,所以只有當(dāng)我們確信算法在分配給該元素或?qū)⒃撛貍鬟f給用戶定義的函數(shù)后才訪問該元素時(shí)爹殊,才應(yīng)將移動(dòng)迭代器傳遞給算法蜕乡。
因?yàn)橐苿?dòng)的對象具有不確定的狀態(tài),所以在對象上調(diào)用std::move是一種危險(xiǎn)的操作梗夸。當(dāng)我們調(diào)用move時(shí)层玲,我們必須絕對確定不能有其他用戶移動(dòng)對象。
在類代碼中明智地使用move可以提供顯著的性能優(yōu)勢反症。隨便在普通用戶代碼中使用(與類實(shí)現(xiàn)代碼相對)辛块,移動(dòng)對象更有可能導(dǎo)致神秘且難以發(fā)現(xiàn)的錯(cuò)誤,而不是應(yīng)用程序性能的任何改進(jìn)铅碍。
在類實(shí)現(xiàn)代碼(例如移動(dòng)構(gòu)造函數(shù)或移動(dòng)賦值運(yùn)算符)之外润绵,只有當(dāng)我們確定需要執(zhí)行移動(dòng)并且保證移動(dòng)是安全的時(shí),才使用std::move胞谈。

引用限定成員函數(shù)

我們以與定義const成員函數(shù)相同的方式指示this的左右值屬性尘盼。我們在參數(shù)列表后面放置一個(gè)引用限定符:

Code:
    class Foo {
    public:
        Foo &operator=(const Foo&) &; // may assign only to modifiable lvalues
            // other members of Foo
        };
        Foo &Foo::operator=(const Foo &rhs) &
        {
            // do whatever is needed to assign rhs to this object
            return *this;
        }

引用限定符可以是&&&,表示這可以分別指向右值或左值。與const限定符一樣,引用限定符只能出現(xiàn)在(非static)成員函數(shù)上叹阔,并且必須出現(xiàn)在函數(shù)的聲明和定義中。
我們可以只在左值上運(yùn)行由&限定的函數(shù)蝙搔,也可以只在右值上運(yùn)行由&&限定的函數(shù):

Code:
    Foo &retFoo(); // returns a reference; a call to retFoo is an lvalue
    Foo retVal();  // returns by value; a call to retVal is an rvalue
    Foo i, j;      // i and j are lvalues
    i = j;         // ok: i is an lvalue
    retFoo() = j;  // ok: retFoo() returns an lvalue
    retVal() = j;  // error: retVal() returns an rvalue
    i = retVal();  // ok: we can pass an rvalue as the right-hand operand to assignment

函數(shù)可以是const和引用限定的。在這種情況下,引用限定符必須遵循const限定符:

Code:
    class Foo {
    public:
        Foo someMem() & const;    // error: const qualifier must come first
        Foo anotherMem() const &; // ok: const qualifier comes first
    };

參考文獻(xiàn)

[1] Lippman S B , Josée Lajoie, Moo B E . C++ Primer (5th Edition)[J]. 2013.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市底桂,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌伪很,老刑警劉巖戚啥,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異锉试,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)览濒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門呆盖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人贷笛,你說我怎么就攤上這事应又。” “怎么了乏苦?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵株扛,是天一觀的道長尤筐。 經(jīng)常有香客問我,道長洞就,這世上最難降的妖魔是什么盆繁? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮旬蟋,結(jié)果婚禮上油昂,老公的妹妹穿的比我還像新娘。我一直安慰自己倾贰,他們只是感情好冕碟,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著匆浙,像睡著了一般安寺。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上首尼,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天我衬,我揣著相機(jī)與錄音,去河邊找鬼饰恕。 笑死挠羔,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的埋嵌。 我是一名探鬼主播破加,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼雹嗦!你這毒婦竟也來了范舀?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤了罪,失蹤者是張志新(化名)和其女友劉穎锭环,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體泊藕,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡辅辩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了娃圆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片玫锋。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖讼呢,靈堂內(nèi)的尸體忽然破棺而出撩鹿,到底是詐尸還是另有隱情,我是刑警寧澤悦屏,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布节沦,位于F島的核電站键思,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏甫贯。R本人自食惡果不足惜吼鳞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望获搏。 院中可真熱鬧赖条,春花似錦、人聲如沸常熙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽裸卫。三九已至仿贬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間墓贿,已是汗流浹背茧泪。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留聋袋,地道東北人队伟。 一個(gè)月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像幽勒,于是被迫代替她去往敵國和親嗜侮。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

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