STL GP 思维 & 6大部件 & Iterator Traits & 容器 & 适配器: Key points

1 深入理解 STL GP 思维: Algorithm 利用 Iterator 操作 Container & iterator_traits

image.png
    1   两种 Iterator

        [1] native pointer

        [2] smart ptr

    2   STL 设计  `2 大 目标, 4 种 方法`

        2大目标

            `关联 type` 用于 
            
                [1] declare obj 
                [2] 作 return type`
        
        4 种方法
        ————————————————————————————————————————————————————————————————————
        [1] 函数模板 实参推导

                限制: 无法解决 关联类型 作 return type
        ————————————————————————————————————————————————————————————————————
        [2-4] class 型 Iterator (关联类型) + 
                
                `no-traits / iterator_traits 泛化 / iterator_traits 偏特化`
        ————————————————————————————————————————————————————————————————————
        [2/3] 效果相同
        
            限制: Iterator 不能为 native pointer

            [3] dif with [2]

                `为 Iterator class 声明 iterator_traits`

                => Iter::value_type 变为 iterator_traits<Iter>::value_type`
        ————————————————————————————————————————————————————————————————————
        [4]

            `Iterator 可为 native pointer`

                dif with [3]
        
                加 2个 iterator_traits 偏特化版本:T* 和 const T*
        ————————————————————————————————————————————————————————————————————
        
        Note
            
            1)  simple Iterator + 单元素容器

                阐述 Algorithm 如何利用 Iterator 操作 Container 中 element`

            2)  typename: `告诉 compiler 其所修饰的 name 是 type`

                用途: declare obj 或 作 return type
        
            3)  `偏特化`
                
                    对 模板参数 `进一步限制`

            4)  const int* 通过 
             
                iterator_traits<T*>         取出 const int 
                iterator_traits<const T*>   取出 int

2 STL 6大部件 & Iterator Traits & OOP 与 GP & 容器 分类 和 关系

STL 6大部件.png
STL 6大部件: 例子.png
image.png
image.png
Iterator Traits.png
Iterator Traits.png
Iterator Traits.png
    1   STL 体系结构 基础:    STL 6大部件 (Components)

            容器   Container : 即 数据结构
            分配器 Allocator
            算法   Algorithm
            迭代器 Iterator
            适配器 Adapter
            仿函数 Functors / Function object

            Data structures + Algorithms = Programs

                (1) `容器` 放 data, data 要占 memory

                (2) 容器 借助 `分配器` 解决掉 memory 问题
                    
                    使 `client 不用 care memory`, 只需 往/从 容器中 放/取 东西即可

                (3) 容器 是 `template class`
                    
                    操作 data 的 `func 一些放 容器 里`; 
                        
                        `一些 以 telmplate function 独立出来`, 就是 `算法`

                (4) 算法 处理 容器 中 的 data, 要借助 
                    
                    `迭代器` 
                    
                        这个 `泛化的 pointer`

                (5) `适配器` : 适配 容器 / 迭代器 / 仿函数

                (6) `仿函数` : 作用像 函数, 可保存 state

                    // eg
                    #include <iostream>
                    #include <vector>
                    #include <algorithm>
                    #include <functional>

                    using namespace std;

                    int main()
                    {
                        int array[5] = {3, 1, 5, 10, 8};
                        vector<int, allocator<int> > vec(array, array + 6);

                          cout << cout_if(vec.begin(), vec.end(), 
                                not1( bind2nd( less<int>() , 6 ) ) ); // 大于等于: not1( less<T> )
                        return 0;
                    }

                选哪种容器 取决于 data 的 分布特性

            Note    
                前闭后开 区间

                    标准库 中用于表现 容器 的 头和尾
                    begin() : 头
                    end()   : 尾 下一个位置
                    
    2   Iterator Traits

            Traits: 从丢给它的东西中 提取出 想要的特征

            Iterator 让算法知道要处理的 data scope

        (1) 算法 问 Iterator: 你的 特征 是什么 ?`

            1) class 型 Iterator 自己可以 回答

            2) non-class / native pointer 型 Iterator 自己无法回答
            
                    借助 `中间层 ( 银弹 silver bullet )` Traits ( `偏特化` ) `替自己回答`

                为 统一接口 -> class 型 Iterator 也 借助 Traits ( 泛化 ) 替自己回答

        (2) Iterator Traits + 泛化 / 偏特化` 作用 

            1) 区分 Iterator 是 class 还是 native pointer, 
        
                区分 native ptr 指向 non-const 还是 const ( 是 T* / const T* )

            2) 提取 class Iterator 5 种 关联类型

            3) 提取 native pointer 的 value type
            
    3   OOP vs. GP

        data / 操作 data 的 func
            
            均放 容器        -  OO
            
            放   容器 / 算法 -   GP
                
                => OO 和 GP 思路不同

        (1) OP 企图 把 data 和 method 关联起来

            list 里有 sort

        (2) GP 企图 把 data 和 methods 分开

            sort 独立出来放在 Algorithms 中, 作 global func

            ::sort( c.begin(), c.end() );

                Containers 和 Algorithms 团队可各自闭门造车, 
                    其间以 Iterators 沟通 即可:
                        Algorithm 通过 Iterators 确定操作范围,  
                            存/取 Container element

            template <typename T>
            inline const T& 
            min(const T& a, const T& b)
            {
                return b < a ? b : a;
            }

            Container: T 类型 怎么规定 < ?
                
                由 T 自己决定 
                    
                    => T 要 重载 `operator <`

            Algorithm: min 不用关注 < 怎么实现

            `STL Algorithm 中 sort 所用 Iterator` 要求 `随机存取 Iterator,  list::iterator 不满足`

                `所有 algrothms`, 其内 `最终涉及 元素本身` 的操作, 无非就是 `比大小`

    4   容器 分类 和 关系

        Gnu2.9.1 中很少有继承
        class A 要用到 class B 功能: B 复合 / 继承 A

        缩排容器间: 复合关系
            set 中 has a rb_tree

    

3.1 vector

image.png
insert.png
insert.png
insert.png
    序列式 容器

    几乎任何 特定 data structure 都是为了 
        实现某种 特定 algorithm

    STL容器 就是实现出 运用最广的一些 data structure

        G4.9版 实现用了很多继承, 很复杂, 但不见得好
    
    1   概述
        (1) vector 是 动态 array, 当 满载 & 继续放元素 时, vector 会 
            
            另寻空间, 并 自动 2 倍 增长

                每次扩充, 有大量的 copy ctor(旧 vector copy 到新 vector)
                    和 dtor ( 旧vector 元素)

        (2) memory 中 不能在原地扩充

            因为 vector 在 memory 某处产生后, 
                
                其后 memory 是否使用, 及 有多大 space 并不知道

                所以, vector 自动扩充 时,

            (1) 另寻一块 2 倍 的空间 
            (2) 把 旧空间元素 拷过去,新元素 紧随 旧元素 后面放 
            (3) 释放原空间

                `重新配置, 移动数据, 释放原空间`

        (3) 用 3 根 pointer 就可控制整个 vector: strat, finish, end_of_storage`**
            
            sizeof( vector_obj ) = 3 * 4 = 12

    2   source code

        `任意容器, 若 空间(vector) / 逻辑(deque) 连续,就必须提供 operator []`**

        vector 初始容量为 0 时, new_size 设为 1; 
            否则, 无法扩充

    3   vector 的 iterator

        vector 是 线性连续空间 
            
            => 其 iterator 为 native ptr = T*

3.2 list

image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
    list 最具代表性


    list 优势
        (1) 插入/删除 1个 元素, 就 配置/释放 1个 元素空间 
            
            - 空间效率高

        (2) 在指定 position 插入/删除, 通常是常数时间 - 时间效率高

    Note:   `prefix/postfix ++ 中 *this 不会唤起 operator* 的 3 种 情形`

        (1) return *this;
            return by reference => 不 唤起 什么

        (2) self tmp = *this;
            
            *this 作 arg of 作 copy ctor

        (3) ++*this;
        
            *this 作 arg of operator++

    2   `operator-> 特性`
        
        -> 作用下去( 即 调 operator->() ) 的 result, 
            
            继续用 -> 作用下去, 直到触及 a pointer to a build-in type

                // construct list
                std::list<Foo>::iterator iter 
                    = list.begin(); 

                iter->method();         // <=>        
                ( &(*iter) )->method(); // <=>
                (*iter).method(); // (*iter) is a Foo_obj

    //------ clear / splice : easy

    template <class T, class Alloc>
    void list<T, Alloc>::
    clear()
    {
        link_type cur_pnode = pnode->next; 
        while (cur_pnode != pnode)          
        {
            link_type tmp = cur_pnode;
            cur_pnode = cur_pnode->next;          
            destory_node(tmp);
        }

        pnode->next = pnode;
        pnode->prev = pnode;
    }

    // 接合 splice = transfer + 适当区间 
    // 把 iter_scope [first, last) elem(s) 接合于 position 之前
    
    // 其他 2 种重载 只要 转化为 区间形式即可
    //      对 [iter, ++iter) / [list2.begin(), list2.end()) 用 transfer 

    template <class T, class Alloc>
    void list<T, Alloc>::
    splice(iterator position, iterator first, iterator last)
    {
        if(first != last)
            transfer(position, first, last);
    }

    template <class T, class Alloc>
    void list<T, Alloc>::
    splice(iterator position, iterator iter)
    {
        // iter/next == position 时, do nothing
        iterator next = ++iter;
        if( iter == position || next == position)
            return; 

        transfer(position, iter, next);
    }

    template <class T, class Alloc>
    void list<T, Alloc>::
    splice(iterator position, list& list2)
    {
        if( ! list2.empty() )
            transfer(position, list2.begin(), list2.end());
    }

    //------ unique: 双指针
    template <class T, class Alloc>
    void list<T, Alloc>::
    unique()
    {
        iterator current = begin();
        iterator tail_off_one = end();

        if (current == tail_off_one)
            return;

        iterator next = current;
        
        while (++next != tail_off_one)   
        {
            if (*next == *current) 
                erase(next);    
            else
                current = next;   

            next = current;       
        }
    }

    //------ 3 merge list2 to list1: 5 iterator
    // (1) 2对 [current, last) 形成 动态处理区间 
    // (2) list2 的 two iterator: next / current2 联动
    template<class T, class Alloc>
    void list<T, Alloc>::
    merge(list<T, Alloc>& list2)
    {
        iterator current1 = begin();
        iterator last1 = end();
        iterator current2 = list2.begin();
        iterator last2 = list2.end();

        //(1) case1: list1/list2 都没 遍历完
        while(current1 != last1 && current2 != last2)
        {
            if(*current2 < *current1) 
            {   // 3 steps
                iterator next = current2;            
                transfer(current1, current2, ++next); 
                current2 = next;                    
            }
            else //=> list1 traverse_iter ++ : ready for the next loop to judge
                ++current1;
        }

        //(2) case2: list2/list1 遍历完/没遍历完(irst2 == last2 / first1 != last1),
        //    则 merge 已完成

        //(3) case3: list1/list2 遍历完/没遍历完,
        //    则 transfer list2 剩余部分 到 list1.end() 之前 
        if(current2 != last2) 
            transfer(last1, current2, last2);
    }

    // reverse: 逐个 transfer [2th, end() ) elem to begin() 前
    template<class T, class Alloc>
    void list<T, Alloc>::
    reverse()
    {
        //---1. list 有 0/1 个 node, do_nothing
        if( pnode->next == pnode || (pnode->next)->next == pnode ) 
            return;
        
        iterator current = begin();
        ++current;

        while(current != end() )
        {
            iterator current_old = current;
            ++current;
            transfer(begin(), current_old, current);
        }
    }
        
    // --- the most important func: not hard
    template<class T, class Alloc>
    void list<T, Alloc>::
    transfer(iterator pos, iterator first, iterator last)
    {
        if(pos != last) // else do_nothing
        {   // 7 steps
            ( *(*last.pnode).prev ).next = pos.pnode;
            ( *(*pos.pnode).prev ).next = first.pnode;
            link_type tmp = (*first.pnode).prev;
            (*first.pnode).prev = (*pos.pnode).prev;
            (*pos.pnode).prev = (*last.pnode).prev;
            tmp->next = last.pnode;
            (*last.pnode).prev = tmp;       
        }
    }

    int main()
    {
        list<int> list1;
        list1.push_back(1);
        list1.push_back(2);
        list1.push_back(3);

        list<int> list2;
        list2.push_back(4);
        list2.push_back(5);
        list2.push_back(6);

        list<int>::iterator ite1_beg = list1.begin();
        list<int>::iterator ite1_end = list1.end();
        list<int>::iterator ite2_beg = list2.begin();
        list<int>::iterator ite2_end = list2.begin();

        list1.merge(list2);
        
        for(ite1_beg = list1.begin(); 
            ite1_beg != list1.end();
            ++ite1_beg)
            std::cout << *ite1_beg << std::endl;
            
        list1.reverse();
        
        for(ite1_beg = list1.begin(); 
            ite1_beg != list1.end();
            ++ite1_beg)
            std::cout << *ite1_beg << std::endl;
    }

    5   重要算法图示

        `transfer 思维 很重要`

        transfer: other 算法 的 基础, 实现不难
        position 与 [first, last) 可 属于 同一 list`

        取成员 . 优先级 高于 解引用 *

3.3 deque 概述

image.png
image.png
image.png
image.png
image.png
    1   
        (1) array vector deque 比较

            array 
            
                线性连续空间 
                
                不能 动态增长

            vector
            
                线性连续空间 
                    
                `向 尾端 动态增长`
                    重配空间/复制元素/释放原空间
                    
                单向开口
                    `尾`部 插入/删除 常数时间

            deque
                `线性 分段连续空间, 对外呈现 整体连续 的假象`
                
                可 向两端 动态增长, 随时可增加 1个分段连续空间 并串接起来
                
                双向开口: `头 和 尾` 插入/移除 常数时间

            相同点
                都可在 任意位置 插/删

        (2) deque (vs. vector)

            1) 头尾 插/删 均常数时间

            2) 没 capacity 概念, 
                
                随时可增加一个 分段连续空间

            3) iterator 为 随机型

                iterator 复杂度高, 非必要, 尽量用 vector, 而非 deque

            4) sort deque
                
                copy 到 vector -> STL sort() -> 再 copy 回来 

    2   中控器 / map: 在 `分段连续 基础上, 维持 整体连续 的 假象`

        (1) 中控器 memory blocks/nodes

            1) node/map type 为 T*/T** 
            期望 指向 deque 某 buf / 中控器 memory 首 node T*

            note: 这里的 node 是指 map blk_mem 的 elem
            而不是 deque_iterator 的 mem data node(map_pointer/T**)

            2) map memory 要增长时, map memory 据 deque 向左还是向右 增长,
            另找一块 更大空间, map 随之指向 新空间 首 node

        (2) 中控器 表示

            template<class T, class Alloc = alloc, size_t Bufsiz = 0>
            class deque
            {
            public:
                typedef T           value_type;
                typedef value_type* pointer;
            protected:
                typedef pointer*    map_pointer;
                
                // 2 mem data: pointer to map memory / map_size
                map_pointer map;    // map : T**
                size_type map_size;
            };

        (3) map 一侧 满载, 且要向 该侧增长时, 另找一块空间 作新 map

        (4) 中控器 / buf / iterator 图`

            deque<int, alloc, 8> deq; // 经一系列操作后有20个元素

    3   iterator

        deque 的 分段连续空间 / buf: deque 元素 的 存储空间

        deque 有若干 大小相同的 分段连续空间 / buf
        buf 大小 : 1个 buf 中 deque elem num


        (1) iterator structure: 4 mem data`

            1) 要能 `指向 current elem`

                T* cur;

                note:

                `start / finish iterator: cur 指向 deque 第 1 有效元素 / last_off_by_one`

            2) 要能 `指向` 所指 buf `边缘`

                T* first
                T* last

            3) 要能 `判断` 自己`是否指向` 所指 buf `边缘`

                cur == first  cur == last

            4) 指向 右 / 左边缘 (last / first) 时, 若 前进/后退,

                要能 `跳到` the next / previous buf

                map_pointer node; // node : T**

        (2) iterator key behavior`

            1) get buf_size

                static size_t buffer_size()
                {
                    deque_buf_size(BufSiz, sizeof(T));
                }

                若 iterator 指定 BufSiz, 则 取指定值;
                否则, // 即 BufSiz = 0
                    若 deque 元素大小 sizeof(T) < 512, 取 512 / sizeof(T);
                    否则, 取 1

            2) 跳到 new_node 指定 buf
         
                void set_node(map_pointer new_node)

            3) ++ --

                self& operator++();  self& operator--()

                `cur :`
                `+1/-1 后/前 在 buf 右/左 边界, 则 跳到 next / previous buf 左/右边界 -> 更新 cur / 更新 cur 再 -1`


                `记 每个 buf 为 [first, last)`
                `cur ++ 前 必然没在 last, 否则, 遇 last 就跳已到 next buf`
                    1] ++cur
                    2] judge whether last
                    yes:
                        跳到 next buf 
                        update cur = first

                // cur -- 前 可能在 first
                    1] 先判 whether first
                    yes:
                        跳到 previous buf
                        update cur = last
                    2] --cur


            4) += n

                iterator 跳 n (= > < 0) 个 elem
                self& operator+=(difference_type n)

    4   deque data structure

        (1) mem data

            1) 要能 跳到指定 buf: 
                
                map_pointer map

            2) 要能 向两侧 扩充 map
                
                map_size 记住 current map size

            3) 要能定位 第1 buf / tail_buf 的 第 1 elem / last_off_by_one
                
                start / finish: 4 pointer

3.4 stack: LIFO

image.png
    从 形象 角度看, deque 左 / 右 侧 为 front() / back(), 
        
        封掉 左侧 / 左进 +  右出, 
    
            且 只留    push  pop  top / push pop front back 3/4 种 operations 
        
                及 empty size 2个 simple func 即为 stack / queue`


    1   stack 无 iterator

    2   stack 定义

        (1) 缺省情况下, stack 底层 data structure 为 deque

        (2) stack 利用其 `底层容器` ( deque / list)  完成其 所有功能`, 
            
            因具有 `修改某物接口, 形成另一种风貌` 的性质
                
                称 `adapter`

        (3) stack obj non-const 时, 
            
            取出的 顶部元素是 reference, 可直接修改 之

template <class T, class Sequence = deque<T> >
class stack
{
    friend bool operator== <> (const stack& const stack&); // 调 Sequence 的 operator==, 实现 略
    friend bool operator< <> (const stack& const stack&);  

protected:
    Sequence c; // 底层容器 c

public:
    typedef typename Sequence::value_type value_type;
    typedef typename Sequence::size_type size_type;
    typedef typename Sequence::reference reference;
    typedef typename Sequence::const_reference const_reference;

    void push(const value_type& x)
        { c.push_back(x); }

    void pop() { c.pop_back(); }

    //  取顶部 reference
    reference 
    top() { return c.back(); }
    
    const_reference 
    top() const { return c.back(); }
    
    // size() / empty 
    bool empty() const { return c.empty(); }
    size_type size() const { return c.size(); }
};
#include <stack>
#include <list>
#include <iostream>
#include <algorithm>

int main()
{
    std::stack<int, std::list<int> > stk; // 以 list 作底层容器 的 stack

    stk.push(1);
    stk.push(5);
    stk.push(10);

    std::cout << stk.size() << std::endl; //3
    std::cout << stk.top()  << std::endl; //10

    stk.pop();
    std::cout << stk.size() << std::endl; //2
    std::cout << stk.top()  << std::endl; //5

    stk.top() = 6;
    stk.push(7);
    stk.pop();
    std::cout << stk.top() << std::endl;  //6
}

3.5 queue: FIFO

image.png

3.6 set


    1   2 点 与 map 相同
        
        [1] key 各不相同

        [2] 元素 据 key 自动排序

    2   source code
    
        set 函数 基本都是 转调 RB-tree 函数
        
        pair<iterator, bool> 
        set<Key, Compare, Alloc>::insert(const value_type& x)
        {
            pair<typename rep_type::iterator, bool> p 
            = t.insert_unique(x);

            return pair<iterator, bool>(p.first, p.second);
        }

3.7 map


    1   元素是 pair, pair.first/second 为 key / value

        typedef pair<const Key, T> value_type;

        用 map 迭代器 不可 / 可 修改 map 的 key / value

        Note
            map 的 iterator 既非 const iterator 也非 mutable iterator`

    2   source code

            // <stl_pair.h> 中 pair 的定义
            template <class T1, class T2>
            struct pair
            {
                typedef T1 first_type;
                typedef T2 second_type;

                T1 first; // note: public
                T2 second;

                pair() : first(T1()), second(T2()) { }
                pair(const T1& a, const T2& b) : first(a), second(b) { }
            };

    下标操作符 operator[]
        
        引用传递, 先据 key 找 value, 再 插

            key 不存在/存在, 插入成功/失败, 
                
                `返回 pair.first = iterator` 指向 `目标 key` 的 `新/旧 elem`

            [] 作左值 -> 写: 插入成功/失败 + 改写 vaule

            [] 作右值 -> 读: 插入成功/失败 + 读 新 unknow_value / 旧 key 的 value`

4 适配器

    1   容器 适配器

        stack queue 都是 adapter

    2   函数对象 adapter

        (1) member

            1) `mem data` 为 `要配接的 Operation 的 copy`
                                            |
                                            |/
                                        less<int>()
                                        
            2) 重载 operator() 
                
                调用 Operation copy 的 operator() 
                    
                    在 para 和 return value 上 动手脚

                [1] 最灵活, 可 配接、配接、再配接

                [2] 价值: `通过 它们之间的 `绑定、否定、组合 等`
                    
                    几乎可 `无限制地` 创造出各种可能的 `表达式`, 搭配 `STL algorithms`

                [3] `期望获得 配接能力 的 component,  本身必须 可配接`

                    1] `1 / 2 元 Functor`    必须继承自  `unary_function / binary_function`

                    2] `non-mem func`       必须以         `ptr_fun` 处理

                    3] `mem func`           必须以         `mem_fun 等` 处理

        2.1 bind2nd()
                                函数调用运算符: operator() (...& arg1 ) { return op_copy(arg1, op_arg2_copy); }
                                                |\
                                                |
            第 1 参数: Operation               |
                            |           binder2nd: 用 2 个 mem data 保存
                            |           |\
                            |           |   值传递 copy
                            |           |
                            |   +   =>  2 个实参 构造 binder2nd<OP> 并 return  
                            |                           
                        强转为 Operation 第 2 形参类型  
                            |\
                            |
                            |
            第 2 参数: 要绑定的参数 

        2.2 用于 `non-mem 函数指针: ptr_fun`

            ptr_fun 是 模板函数
                
                para 是 函数指针 类型 
                
                return 函数对象 pointer_to_unary_function<Arg, Result>
                            |
                            |/
                            1]  `wrap 函数指针`
                                  |
                                  |
                                  |/
                                mem data 用于存 函数指针 的 copy 
                                
                            2]  函数调用运算符 operator() 
                                    
                                    `转调` 函数指针 copy 的 函数调用运算符 operator() 

                                void 
                                print(int value)
                                {
                                    std::cout << value << ' ';
                                }

                                int main()
                                {
                                    int a[3] = { 1,3,5 };
                                    std::vector<int> vec(a, a + 3);

                                    // interface
                                    for_each(vec.begin(), vec.end(), 
                                             ptr_fun(&print) );
                                }

        2.3 适配 `成员函数 指针` : `mem_fun / mem_fun_ref / mem_fun1 / mem_fun_ref`
                
            结果是 作 `函数对象` 用, 以 搭配 GP algorithm

            (1) 4 种函数

                1) 带/不带 1: 有1个参数 / 无参

                2) 带/不带 ref: ref/ptr 调 operator()

            (2) 思想

                wrap + 转调 operator()
                
            (3) 应用: 用 `GP algorithm 实现 多态调用` 
                    
                    `GP 与 多态` 间 重要接轨
            
                1]  容器 元素 A* 或  A&
                    
                2]  适配 `vf 指针`
                    
                    std::vector<Shape*> vec;
                    // ...
                    for_each(vec.begin(), vec.end(), std::mem_fun( &Shape::display ) );
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,036评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,046评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,411评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,622评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,661评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,521评论 1 304
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,288评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,200评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,644评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,837评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,953评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,673评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,281评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,889评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,011评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,119评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,901评论 2 355