The C++ standard library(侯捷/孟岩 译) 07--迭代器

7.1 迭代器头文件

所有容器定义各自的迭代器类型,使用某容器迭代器时不需包含特定文件。
  但有些迭代器,如逆向迭代器,被定义于头文件<iterator>中。

7.2 iterator categories

p7-1.png
t7-1.png
7.2.1 input iterator

input iterator只能一次一个地向前读取元素。
一旦input stream读入一个字后,下次读取时就返回另一个字。

t7-2.png

使用前置运算性能更好。

7.2.2 output iterator

t7-3.png

note:output iterator无comparison操作,无法检验output迭代器是否有效或“写入”是否成功。

7.2.3 forward iterator

forward iterator是input iterator的全部功能和部分output iterator功能。


t7-4.png

与input/output iterator不同,forward iterator能多次指向同一群集的同一元素,并能进行多次处理。

output iterator不支持检查是否到达序列尾端。

对forward 迭代器解引用时需确保其有效性。
7.2.4 bidirectional iterator

bidirectional iterator在forward iterator基础上增加了对逆向遍历的支持,即支持递减操作符。


t7-5.png
7.2.5 random access iterator

random access iterator在bidirectional iterator基础上支持随机存取。即支持“迭代器算术运算”。
以下对象/类型支持random access iterator:

  • 可随机存取的容器(vector/deque)
  • string (字符串,string ,wstring)
  • 一般array(指针)


    t7-6.png
// 说明random access iterator的特殊方法
// iter/itercat.cpp

#include <vector>
#include <iostream>
using namespace std;

int main()
{
    vector<int> col1;

    // insert elements from -3 to 9
    for (int i = -3; i <= 9; ++i)
    {
        col1.push_back(i);
    }

    /* print number of elements by processing the distance between beginning and end
     * - NOTE: uses operator- for iterators
     */
    cout << "number/distance: " << col1.end() - col1.begin() << endl;

    /* print all elements
     * - NOTE: uses operator < instead of operator !=
     */
    vector<int>::iterator pos;
    for (pos = col1.begin(); pos < col1.end(); ++pos)
    {
        cout << *pos << ' ';
    }
    cout << endl;

    /* print all elements 
     * - NOTE: uses operator[] instead of operator *
     */
    for (int i = 0; i < col1.size(); ++i)
    {
        cout << col1.begin()[i] << ' ';
    }
    cout << endl;

    /* print every second element
     * - NOTE: uses operator +=
     */
    for (pos = col1.begin(); pos < col1.end() - 1; pos += 2)
    {
        cout << *pos << ' ';
    }
    cout << endl;
}

程序说明:有NOTE标识的操作只适用于random access iterator。

output:
itercat.png
7.2.6 vector迭代器increment / decrement

一般可递增或递减暂时性iterator,但对于vector/string则不行。

eg:
vector<int> col;
//...
if (col.size() > 1){
    sort(++col.begin(), col.end() );
}

通常编译sort()会失败,若换deque取代vector则可通过编译,
  有时vector也可通过编译——取决于vector具体实现。

为保证可移植性,应使用辅助对象,相关结构改成:
if (col.size()  > 1){
    vector<int>::iterator beg = col.begin();
    sort(++beg, col.end() );
}

主要原因:vector iterator常被实现为一般指针,C++不允许修改任何 基本类型(含指针)的暂时值,对struct/class则可以。

7.3 迭代器相关辅助函数

C++标准库为迭代器提供三个辅助函数:advance()、distance()、iter_swap()。

7.2.1 advance() 可使迭代器前进/后退
#include <iterator>
void advance (InputIterator& pos, Dist n)

使input iterator前进/后退 n个元素;
对bidirectional/random access iterator,n可为负值表后退;
Dist是template类型。通常 是整数,因为会调用<、++、--等,及和0比较;

不检查迭代器是否超过end(),
  因为迭代器通常不知道其所操作的容器。

对random access iterator,advance()调用pos+=n,
  对其它类型迭代器则调用++pos或--pos共n次。

若希望程序能方便更换容器和迭代器种类,应用advance()而不是operator+=。

但对于不提供random access iterator的容器,性能会变差。
  另,advance()无返回值,operator+=有返回值,后者更强大。

eg:
// advance()运用示例
// iter/advance1.cpp

#include <iostream>
#include <list>
#include <algorithm>
using namespace std;

int main()
{
    list<int> col1;

    // insert elements from 1 to 9
    for (int i = 1; i <= 9; ++i)
    {
        col1.push_back(i);
    }

    list<int>::iterator pos = col1.begin();

    // print actual element
    cout << *pos << endl;

    // step one element backward
    advance (pos , -1);

    // print actual element
    cout << *pos << endl;

    // step three element forward
    advance (pos , 3);

    // print actual element
    cout << *pos << endl;
}

output:
advance1.png
7.3.2 distance()处理迭代器间的距离
#include <iterator>
Dist distance (InputIterator pos1, InputIterator pos2)

返回input iterator pos1和pos2的距离;
pos1和pos2须指向同一容器;
若不是random access iterator,则从pos1须能到达pos2;
返回值Dist类型有迭代器决定:iterator_traits<InputIterator>::difference_type

对于random iterator直接返回pos2-pos1,
  对于其它iterator则不断递增pos1直到pos2止,然后返回递增次数。

eg:
// distance()示例
// iter/distance.cpp

#include <iostream>
#include <list>
#include <algorithm>
using namespace std;

int main()
{
    list<int> col1;

    // insert elements from -3 to 9
    for (int i = -3; i <= 9; ++i)
    {
        col1.push_back(i);
    }

    // search elements with value 5
    list<int>::iterator pos;
    pos = find(col1.begin(), col1.end(),    // range
            5); // value

    if (pos != col1.end() )
    {
        // process and print difference from the beginning 
        cout << "difference between beginning and 5: "
            << distance(col1.begin(), pos) << endl;
    }
    else
    {
        cout << "5 not found" << endl;
    }
}

output:
distance.png

同advance(),若希望方便更换容器类型和迭代器类型,应用distance()而不是operator-。
对non-random iterator,当pos1在pos2之后时,会导致未定义行为。

7.3.3 iter_swap()可交换两迭代器所指内容
#include <algorithm>
void iter_swap (ForwardIterator1  pos1, ForwardIterator2  pos2)
交换pos1和pos2所指的值;
pos1和pos2的类型不必相同但所指的两个值必须可相互赋值。

eg:
// iter/swap1.cpp

#include <iostream>
#include <list>
#include "../stl/print.hpp"
using namespace std;

int main()
{

    list<int> col1;

    // insert elements from 1 to 9
    for (int i = 1; i <= 9; ++i)
    {
        col1.push_back(i);
    }

    PRINT_ELEMENTS(col1);

    // swap first and second value
    iter_swap (col1.begin(), ++col1.begin());

    PRINT_ELEMENTS(col1);

    // swap first and last value
    iter_swap(col1.begin(), --col1.end());

    PRINT_ELEMENTS(col1);
}

output:
swap1.png

7.4 Iterator adapter

此类特殊迭代器使得算法能够以reverse mode / insert mode进行工作,也可和stream搭配工作。

7.4.1 reverse iterator

reverse iterator是一种配接器,重定义递增、递减运算,使其行为正好相反。

// iter/reviter1.cpp

#include <iostream>
#include <list>
#include <algorithm>
using namespace std;

void print (int elem)
{
    cout << elem << ' ';
}

int main()
{
    list<int> col1;

    // insert elements from 1 to 9
    for (int i = 1; i <= 9; ++i)
    {
        col1.push_back(i);
    }

    // print all elements in normal order
    for_each (col1.begin(), col1.end(),     // range
            print); // operation
    cout << endl;

    // print all elements in reverse order
    for_each (col1.rbegin(), col1.rend(),   // range
            print); // operation
    cout << endl;
}

output:
reviter1.png
  • 迭代器和逆向迭代器
    可将一般迭代器转换为reverse iterator,但那个迭代器须可双向移动。且,转换前后迭代器的逻辑位置发生了变化
// iter/reviter2.cpp

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main()
{
    vector<int> col1;

    // insert elements from 1 to 9
    for (int i = 1; i <= 9; ++i)
    {
        col1.push_back(i);
    }

    // find position of element with value 5
    vector<int>::iterator pos;
    pos = find (col1.begin(), col1.end(), 5);

    // print value to which iterator pos refers
    cout << "pos: " << *pos << endl;

    // convert iterator to reverse iterator rpos
    vector<int>::reverse_iterator rpos(pos);

    // print value to which reverse iterator rpos refers
    cout << "rpos: " << *rpos << endl;
}

output:

reviter2.png

迭代器转换为reverse iterator后逻辑位置变化的原因:区间半开性,即实际上reverse iterator倒置了半开原则。
p7-3.png

p7-4.png

rbegin() 即 container::reverse_iterator(end() ),
rend() 即 container::reverse_iterator(begin() )。

但若是一对迭代器定义的区间,变换操作就很简单且元素有效。

// iter/reviter3.cpp

#include <iostream>
#include <deque>
#include <algorithm>
using namespace std;

void print(int elem)
{
    cout << elem << ' ';
}

int main()
{
    deque<int> col1;

    // insert elements from 1 to 9
    for (int i = 1; i <= 9; ++i)
    {
        col1.push_back(i);
    }

    // find position of element with value 3
    deque<int>::iterator pos1;
    pos1 = find (col1.begin(), col1.end(), 2);

    // find positon of element with value 7
    deque<int>::iterator pos2;
    pos2 = find (col1.begin(), col1.end(), 7);
    
    // print all elements in range [pos1, pos2)
    for_each (pos1, pos2, print);
    cout << endl;

    // convert iterator to reverse iterator
    deque<int>::reverse_iterator rpos1(pos1);
    deque<int>::reverse_iterator rpos2(pos2);

    // print all elements in range [pos1, pos2) in reverse order
    for_each (rpos2, rpos1, print);
    cout << endl << endl;

    for_each (rpos1, rpos2, print);
    cout << endl;
}

output:
reviter3.png
  • base()将reverse iterator转换为 一般迭代器
    base()函数可见STL源码剖析。
// iter/reviter4.cpp

#include <iostream>
#include <list>
#include <algorithm>
using namespace std;

int main()
{
    list<int> col1;

    // insert elements from 1 to 9
    for (int i = 1; i <= 9; ++i)
    {
        col1.push_back(i);
    }

    // find position of element with value 5
    list<int>::iterator pos;
    pos = find (col1.begin(), col1.end(), 5);

    // print value of the element
    cout << "pos: " << *pos << endl;

    // convert iterator to reverse iterator
    list<int>::reverse_iterator rpos(pos);

    // print value of the element to whcih the reverse iterator refers
    cout << "rpos: " << *rpos << endl;

    // convert reverse iterator back to normal iterator
    list<int>::iterator rrpos;
    rrpos = rpos.base();

    // print value of the element to which the normal iterator refers
    cout << "rrpos: " << *rrpos << endl;
}

output:
reviter4.png
7.4.2 insert iterator

也称为inserter。通过inserter,算法可insert而非overwrite。
inserter隶属于output iterator,故只提供assignment。

通常算法会赋值给iterator,如copy()算法:
namespace std
{
    template<class Inputiterator, class Outputiterator>
    Outputiterator copy (Inputiterator from_pos,  // beginning of source
                        Inputiterator from_end,  // end of source
                        OutputIterator to_pos)  // beginning of dest
    {
        while (from_pos != from_end)
        {
            *to_pos = *from_pos;  / copy value
            ++from_pos;  // increment iterator
            ++to_pos;
        }
    }
}

note: *to_pos = value;语句,insert iterator将此类赋值操作转化为插入操作。
    对于insert iterator的转化过程:
      首先operator* 返回iterator当前位置(insert iterator中视为一个 no-op,只简单传回*this。),
      然后operator=赋值(调用容器的push_back()/push_front()/insert() )。
t7-7.png
  • insert iterator种类
    t7-8.png

    由于push_back()只存在于vector/deque/list/string中,故C++标准库中只有这些容器支持back_inserter。
// iter/backins.cpp

#include <iostream>
#include <vector>
#include <algorithm>
#include "../stl/print.hpp"
using namespace std;

int main()
{
    vector<int> col1;

    // create back inserter for col1
    // - inconvenient way
    back_insert_iterator<vector<int> > iter(col1);

    // insert element with the usual iterator interface
    *iter = 1;
    iter++;
    *iter = 2;
    iter++;
    *iter = 3;

    PRINT_ELEMENTS(col1);

    // create back inserter and insert elements
    // - convenient way
    back_inserter(col1) = 44;
    back_inserter(col1) = 55;

    PRINT_ELEMENTS(col1);

    // use back inserter to append all elements again
    // - reverse enough memory to avoid realllocation
    col1.reserve(2*col1.size() );
    copy(col1.begin(), col1.end(),  // source
            back_inserter(col1) );  // destination

    PRINT_ELEMENTS(col1);
}

output:
backins.png

同理push_front()只在deque/list中有实现,故front_inserter()只支持deque/list。

// iter/frontins.cpp

#include <iostream>
#include <list>
#include <algorithm>
#include "../stl/print.hpp"
using namespace std;

int main()
{
    list<int> col1;

    // create front inserter for col1
    // - inconvenient way
    front_insert_iterator<list<int> > iter(col1);

    // insert elements with the usual iterator interface
    *iter = 1;
    iter++;
    *iter = 2;
    iter++;
    *iter = 3;

    PRINT_ELEMENTS(col1);

    // create front inserter and insert elements
    // - convenient way
    front_inserter(col1) = 44;
    front_inserter(col1) = 55;

    PRINT_ELEMENTS(col1);

    // use front inserter to insert all elements again
    copy (col1.begin(), col1.end(), front_inserter(col1));

    PRINT_ELEMENTS(col1);
}

output:
frontins.png

general inserter根据两个参数初始化:容器和待插入位置。

所有容器都提供insert(),故适用于所有容器,
  注意“待插入位置”对关联式容器只是提示。

插入操作完成后,general inserter会获得被插入元素的位置,即
    pos = container.insert(pos, value);
    ++pos;
如此是为了确保该迭代器的位置始终有效。

// iter/inserter.cpp

#include <iostream>
#include <set>
#include <list>
#include "../stl/print.hpp"

using namespace std;

int main()
{
    set<int> col1;

    // create insert iterator for col1;
    // - inconvenient way
    insert_iterator<set<int> > iter(col1, col1.begin());

    // insert elements with the usual iterator interface
    *iter = 1;
    iter++;
    *iter = 2;
    iter++;
    *iter = 3;

    PRINT_ELEMENTS(col1, "set: ");

    // create inserter and insert elements
    // - convenient way
    inserter(col1, col1.end()) = 44;
    inserter(col1, col1.end()) = 55;

    PRINT_ELEMENTS(col1, "set: ");

    // use inesrter to insert all elements into a list
    list<int> coll2;
    copy(col1.begin(), col1.end(), inserter(coll2, coll2.begin()));

    PRINT_ELEMENTS(coll2, "list: ");

    // use inserter to reinsert all elements into the list before the second element
    copy (col1.begin(), col1.end(), inserter(coll2, ++coll2.begin()));

    PRINT_ELEMENTS(coll2, "list: ");
}

output:
inserter.png
7.4.3 stream iterator

一个istream iterator可从input stream中读,
一个ostream iterator可对output stream写。

  • ostream iterator
    ostream iterator与insert iterator类似,区别在于ostream迭代器将赋值操作转化为operator<<。
    t7-9.png
// iter/ostriter.cpp

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;

int main()
{
    // create ostream iterator for stream cout
    // - value are separated by a newline charcter
    ostream_iterator<int> intWriter(cout, "\n");

    // write elements with the usual iterator interface
    *intWriter = 42;
    intWriter++;
    *intWriter = 77;
    intWriter++;
    *intWriter = -5;

    // create collection with elements from 1 to 9
    vector<int> col1;
    for (int i = 1; i <= 9; ++i)
    {
        col1.push_back(i);
    }

    // write all elements without any delimiter
    copy(col1.begin(), col1.end(), ostream_iterator<int>(cout) );
    cout << endl;

    // write all elements with "<" as delimiter
    copy(col1.begin(), col1.end(), ostream_iterator<int>(cout, "<") );
    cout << endl;
}

output:

ostriter.png

note:分隔符delimiter类型是const char*,若传入一个string对象,须调用c_str()成员函数以获得正确类型。

  • istream iterator
产生istream iterator须提供一个input stream作为参数,iterator从中读数据。

然而,读操作可能失败(文件尾部或读错误等原因),算法也需直到区间是否到终点。
  故可用一个end-of-stream迭代器,该迭代器有istream的default ctor生成,
    只要任何一个读取失败,则所有istream iterator会变成end-of-stream iterator。
    故每次读操作后,须将istream iterator和end-of-istream iterator比较。
t7-10.png

两个istream iterator相等条件:

二者都是end-of-stream iterator或 二者都可进行读操作且指向相同stream。

// iter/istriter.cpp

#include <iostream>
#include <iterator>
using namespace std;

int main()
{
    // create istream iterator that reads integers from cin
    istream_iterator<int> intReader(cin);

    // create end-of-stream iterator
    istream_iterator<int> intReaderEOF;

    /* while able to read tokens with istream iterator
     * write them twice
     */
    while (intReader != intReaderEOF)
    {
        cout << "once: " << *intReader << endl;
        cout << "once again: " << *intReader << endl;
        ++intReader;
    }
}

output:
image.png

stream iterator的另个例子:

// iter/advance2.cpp

#include <iostream>
#include <string>
#include <algorithm>
#include <iterator>
using namespace std;

int main()
{
    istream_iterator<string> cinPos(cin);
    ostream_iterator<string> coutPos(cout, " ");

    /* while input is not at the end of the file
     * - write every third string
     */
    while (cinPos != istream_iterator<string>() )
    {
        // ignore the following two string
        advance(cinPos, 2);

        // read and write the third string
        if (cinPos != istream_iterator<string>())
        {
            *coutPos++ = *cinPos++;
        }
    }
    cout << endl;
}

output:
advance2.png

7.5 iterator traits

根据不同iterator对operation重载,具体实现通过iterator tag和trait(由<iterator>提供)实现重载。
C++标准库为每种iterator提供一个iterator tag作为其label:

namespace std
{
    struct output_iterator_tag{};
    struct input_iterator_tag{};
    struct forward_iterator_tag : public input_iterator_tag{};
    struct bidirectional_iterator_tag : public forward_iterator_tag{};
    struct random_access_iterator_tag : public bidirectional_iterator_tag{};
}

若进行泛型编程,需要了解iterator所指元素类型,C++标准库提供一种template结构定义 iterator trait:

namespace std
{
    template<class T>
    struct iterator_traits
    {
        typedef typename T::value_type  value_type;
        typedef typename T::difference_type  difference_type;
        typedef typename T::iterator_category  iterator_category;
        typedef typename T::pointer pointer;
        typedef typename T::reference  reference;
    };
}
7.5.1 为iterator编写泛型函数
  • 运用iterator type
eg1:以元素类型为类型的零时变量。
    typename std::iterator_traits<T>::value_type tmp;

eg2:元素环形移动。
template<class Forwarditerator>
void shift_left (Forwarditerator beg, Forwarditerator end)
{
    // temporary variable for first element
    typedef typename
      std::iterator_traits<Forwarditerator>::value_type value_type;
    
    if (beg != end)
    {
        // save value of first element
        value_type tmp(*beg);
        // shift following values
        //...
    }
}
  • 运用iterator category
    对不同iterator类型进行不同的实现,需两步:
1. 把template函数将iterator类型做为参数,调用另个函数。eg:
    template <class iterator>
    inline void foo(iterator beg, iterator end)
    {
        foo(beg,end,
            std::iterator_traits<iterator>::iterator_category() );
    }

2. 针对不同iterator实现不同的 步骤一中所调用的函数。
    只有“非派生自其它iterator类型”的iterator类型,才要提供特殊版本。eg:
    // foo() for bidirectional iterator
    template <class BiIterator>
    void foo(BiIterator beg, BiIterator end, 
              std::bidirectioanl_iterator_tag){  // ...}

    // foo() for random access iterator
    template <class RaIterator>
    void foo (RaIterator beg, RaIterator end, 
              std::random_access_iterator_tag){  //...}
  • distance() 实现
// general distance()
template <class iterator>
typename std::iterator_traits<iterator>::difference_type
distance (iterator pos1, iterator pos2)
{
    return distance(pos1, pos2,
            std::iterator_traits<iterator>::iterator_category() );
}

// distance() for random access iterator
template <class Raiterator>
typename std::iterator_traits<Raiterator>::difference_type
distance (Raiterator pos1, Raiterator pos2,
        std::random_access_iterator_tag)
{
    return pos2 - pos1;
}

// distance() for input,forward, and bidirectional iterator
template <class Initerator>
typename std::iterator_traits<Initerator>::difference_type
distance(Initerator pos1, Initerator pos2,
        std::input_iterator_tag)
{
    typename std::iterator_traits<Initerator>::difference_type d;
    for ( d = 0; pos1 != pos2; ++pos1, ++d){;}
    return d;
}
7.5.2 user-defined iterator

自定义iterator需要为iterator提供traits,有两种可行方法:

  • 1. 提供一个特殊版本的iterator_traits结构。
  • 2. 提供必要的五种类型定义,如iterator_traits结构所述。
C++标准库提供了一个特殊的基础类别iterator<>,用于定义自定义iterator。
    class Myiterator 
        : public std::iterator<std::bidirectional_iterator_tag, 
                              type, std::ptrdiff_t, type*, type&>{ //...};
    第一个参数定义iterator类型,第二个参数定义元素类型,第三个参数定义距离类型,
    第四个参数定义pointer类型,第五个参数定义reference类型。
    后三个参数有默认值ptrdiff_t,type*,type&。

eg:关联式容器的insert iterator

// 关联式容器的 insert iterator
// iter/assoiter.hpp

#include <iterator>

/* template class for insert iterator for associative containers */
template <class Container>
class asso_insert_iterator :
    public  std::iterator<std::output_iterator_tag, void, void, void, void>
{
    protected:
        Container& container;   //container in which element are inserted
    
    public:
        // constructor
        explicit asso_insert_iterator (Container& c) : container(c){}

        // assignment operator
        // - inserts a value into the container
        asso_insert_iterator<Container>&
            operator= (const typename Container::value_type& value)
            {
                container.insert(value);
                return *this;
            }

        // dereference is a no-op that returns the iterator itself
        asso_insert_iterator<Container>& operator*() 
        {
            return *this;
        }

        // increment operation is a no-op that returns the iterator itself
        asso_insert_iterator<Container>& operator++()
        {
            return *this;
        }
        asso_insert_iterator<Container>& operator++(int)
        {
            return *this;
        }
};

/* convenience function to create the inserter */
template <class Container>
inline asso_insert_iterator<Container> asso_inserter(Container& c)
{
    return asso_insert_iterator<Container>(c);
}


使用:
// iter/assoiter.cpp

#include <iostream>
#include <set>
#include <algorithm>
using namespace std;

#include "../stl/print.hpp"
#include "assoiter.hpp"

int main()
{
    set<int> col1;

    // create inserter for col1
    // -inconvenient way
    asso_insert_iterator<set<int> > iter(col1);

    // insert elements with the usual iterator interface
    *iter = 1;
    iter++;
    *iter = 2;
    iter++;
    *iter = 3;

    PRINT_ELEMENTS(col1);

    // create inserter for col1 and insert elements
    // - convenient way
    asso_inserter(col1) = 44;
    asso_inserter(col1) = 55;

    PRINT_ELEMENTS(col1);

    // use inserter with an algorithm
    int vals[] = {33, 67, -4, 13, 5, 2};
    copy(vals, vals + sizeof(vals)/sizeof(vals[0]), asso_inserter(col1));
    PRINT_ELEMENTS(col1);
}

output:
assoiter.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,928评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,192评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,468评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,186评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,295评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,374评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,403评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,186评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,610评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,906评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,075评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,755评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,393评论 3 320
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,079评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,313评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,934评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,963评论 2 351

推荐阅读更多精彩内容