C++设计模式(全网最通俗易懂的设计模式进阶)

前言

鉴于架构的重要性,在一个项目开始的时候,在b站上看了一个使用Java做架构的案例,但由于自己对java的语言并不是很熟悉,加上编译环境也没有,因此,就有了下面一个念头,使用c++,将该课程的一个设计模式进行复现。

项目背景

使用c++将完成一个计算器的功能。用户通过在键盘上输入运算符和两个数字,点击确认计算出该运算的结果。实际效果如图:

image

计算器运行结果

0基础的玩法

这个版本就如同绝大多数初学者一样,在hello world的基础上做了一个只是实现功能的,没有任何。算法或者说逻辑可言。


#include <iostream>
#include <sstream>
#include <iomanip>
using namespace std;
/*
实现一个简易的计算器
版本:v1.0
时间:2022年1月21日14:30:12
开发者:董康乐
陕西 西安

TODO v1.0 引出需求(计算器的设计)
TODO 问题:可扩展性、可维护性、高耦合性。代码不规范问题
*/

void main()
{
    std::cout << "第一版计算器v1.0" << std::endl;
    /*-----------------------------------------*/
    //定义字符和数字
    char Operator;//加减乘除四种
    double aaa = 0.0;
    double bbb = 0.0;
    std::cout << "请输入运算符:" << std::endl;
    cin >> Operator;
    std::cout << "请输入第一个数字:" << std::endl;
    cin >> aaa;
    std::cout << "请输入第二个数字:" << std::endl;
    cin >> bbb;
    /*-----------------------------------------*/
    //判断运算符 并在特定的运算符内部完成运算
    double ccc;
    if (Operator == '+')
    {
        ccc = aaa + bbb;
    }
    else
    {
        if (Operator == '-')
        {
            ccc = aaa - bbb;
        }
        else
        {
            if (Operator == '*')
            {
                ccc = aaa * bbb;
            }
            else
            {
                if (Operator == '/')
                {
                    ccc = aaa / bbb ;
                }
            }
        }
    }
    /*-----------------------------------------*/
    cout << "运算结果为:" << std::to_string(ccc);//输出结果

    std::system("pause");
}

总结:上面的代码存着很多问题,除了本身代码的相关性特别强,如果需要增加或者修改功能,需要动大量的代码以外,其命名的规范性以及容错性都没有得到解决,比如说之前要是除法中分母为零的情况。为此我们有了下面的一个升级版v1.1版本。

1 解决容错和代码命名规范


#include <iostream>
#include <sstream>
#include <iomanip>
using namespace std;
/*
实现一个简易的计算器
版本:v1.1
时间:2022年1月21日14:30:12
开发者:董康乐
陕西 西安

TODO v1.0 引出需求(计算器的设计)
解决  代码不规范  代码容错性极差的问题
TODO 问题:可扩展性、可维护性、高耦合性 
*/

void main()
{
    std::cout << "第一版计算器v1.0" << std::endl;
    /*-----------------------------------------*/

    try{
        //定义字符和数字
        char Operator;//加减乘除四种
        double Numb0 = 0.0;
        double Numb1 = 0.0;
        std::cout << "请输入运算符:" << std::endl;
        cin >> Operator;
        std::cout << "请输入第一个数字:" << std::endl;
        cin >> Numb0;
        std::cout << "请输入第二个数字:" << std::endl;
        cin >> Numb1;
        /*-----------------------------------------*/
        //判断运算符 并在特定的运算符内部完成运算
        double result;
        if (Operator == '+')
        {
            result = Numb0 + Numb0;
        }
        else
        {
            if (Operator == '-')
            {
                result = Numb0 - Numb0;
            }
            else
            {
                if (Operator == '*')
                {
                    result = Numb0 * Numb1;
                }
                else
                {
                    if (Operator == '/')
                    {
                        if (Numb1 == 0)
                        {
                            throw "Division by zero condition!";
                        }
                        result = Numb0 / Numb1 ;
                    }
                }
            }
        }
        /*-----------------------------------------*/
        cout << "运算结果为:" << std::to_string(result);//输出结果
    }
    catch (const char* msg) {
        cerr << msg << endl;
    }

    std::system("pause");
}

总结:这个版本中我们使用了try、throw、catch等方式。对代码的容错性进了进行提高,并且将命名的规范性。进一步修改。

2 客户端分离

在这个版本中,我们将客户端进行了分离。将客户端的功能在单独的一个类内实现。这么多可扩充性。高融合性等得到一定程度的解决。

main.cpp


/*
实现一个简易的计算器
版本:v1.2
时间:2022年1月21日13:50:19
开发者:董康乐
陕西西安

TODO v1.2 引出需求(计算器的设计)
使用 面向对象的思维解决 分离客户端和业务端 只是解决部分问题
TODO 问题:可扩展性、可维护性、高耦合性 解决70%
*/

#include <iostream>
#include <sstream>
#include <iomanip>
#include "Operator.h"

using namespace std;

void main()
{
    std::cout << "第一版计算器v1.2" << std::endl;
    /*-----------------------------------------*/

    try{
        //客户端
        //定义字符和数字
        char operatorFunction;//加减乘除四种
        double Numb0 = 0.0;
        double Numb1 = 0.0;
        std::cout << "请输入运算符:" << std::endl;
        cin >> operatorFunction;
        std::cout << "请输入第一个数字:" << std::endl;
        cin >> Numb0;
        std::cout << "请输入第二个数字:" << std::endl;
        cin >> Numb1;
        /*-----------------------------------------*/
        //业务端
        double result = Operator::countResult(operatorFunction, Numb0, Numb1);
        cout << "运算结果为:" << std::to_string(result);//输出结果
    }
    catch (const char* msg) {
        cerr << msg << endl;
    }

    std::system("pause");
}

实际运算的类为Operator类

其中:Operator.h


#pragma once
#include <iostream>
#include <sstream>
#include <iomanip>
using namespace std;
class Operator
{
public:
    Operator();
    ~Operator();

    static double countResult(char Operator, double Numb0, double Numb1);
};

Operator.cpp如下


/*
功能:业务端的分离
*/
#include "Operator.h"

Operator::Operator()
{

}

Operator::~Operator()
{
}

double Operator::countResult(char Operator, double Numb0, double Numb1)
{
    //判断运算符 并在特定的运算符内部完成运算
    double result;
    if (Operator == '+')
    {
        result = Numb0 + Numb0;
    }
    else
    {
        if (Operator == '-')
        {
            result = Numb0 - Numb0;
        }
        else
        {
            if (Operator == '*')
            {
                result = Numb0 * Numb1;
            }
            else
            {
                if (Operator == '/')
                {
                    if (Numb1 == 0)
                    {
                        throw "Division by zero condition!";
                    }
                    result = Numb0 / Numb1;
                }
            }
        }
    }
    return result;
}

3解决高耦合性问题

使用了对虚函数的继承的方式来实现用一个虚函数,完成整体的架构,在不同的基层函数里面完成独立的运算,解决高高耦合性问题。在这个基础上,如果我们要增加或者减少一个单独的功能,那么就会变得非常容易。在某个单独实现的内内也可以独立的进行增加内容。

主函数main.cpp


/*
实现一个简易的计算器
版本:v1.3
时间:2022年1月21日16:08:48
开发者:董康乐
陕西西安

TODO v1.3 引出需求(计算器的设计)
使用  解决可扩展性、可维护性问题
TODO 问题:高耦合性 解决70%
*/

#include <iostream>
#include <sstream>
#include <iomanip> 
#include "Operator.h"
#include "OperatorMul.h"
#include "OperatorAdd.h"
#include "OperatorSub.h"
#include "OperatorDiv.h"

using namespace std;

void main()
{
    std::cout << "第一版计算器v1.3" << std::endl;
    /*-----------------------------------------*/

    try{
        //客户端
        //定义字符和数字
        char operatorFunction;//加减乘除四种
        double Numb0 = 0.0;
        double Numb1 = 0.0;
        std::cout << "请输入运算符:" << std::endl;
        std::cin >> operatorFunction;
        std::cout << "请输入第一个数字:" << std::endl;
        cin >> Numb0;
        std::cout << "请输入第二个数字:" << std::endl;
        cin >> Numb1;
        /*-----------------------------------------*/
        //业务端
        //只面向抽象,面向高层 ,面向接口的感觉

        Operator  *oper =NULL;
        //Operator  *oper = new OperatorAdd();
        switch (operatorFunction)
        {
        case '+':
            oper = new OperatorAdd();
            break;
        case '-':
            oper = new OperatorSub();
            break;
        case '*':
            oper = new OperatorMul();
            break;
        case '/':
            oper = new OperatorDiv();
            break;
        }
        oper->setNumb0(Numb0);
        oper->setNumb1(Numb1);
        cout << "运算结果为:" << std::to_string(oper->countResult());//输出结果
    }
    catch (const char* msg) {
        cerr << msg << endl;
    }

    std::system("pause");
}

operator类


#pragma once
class Operator
{
public:
    Operator();
    ~Operator();
    double getNumb0();
    double getNumb1();

    void setNumb0(double);
    void setNumb1(double);

    virtual double countResult()=0;
private :
    double numb0;
    double numb1;

};


/*
首先定义规则。【计算器业务的规则】
抽象的:numb0、numb1、计算使用 依赖导致原则
*/
#include "Operator.h"

Operator::Operator()
{
}

Operator::~Operator()
{
}

double Operator::getNumb0()
{
    return numb0;
}

double Operator::getNumb1()
{
    return numb1;
}

void Operator::setNumb0(double numb0)
{
    this->numb0 = numb0;
}
void Operator::setNumb1(double numb1)
{
    this->numb1 = numb1;
}


/*
使用 单一职责原则
加法业务的逻辑 具体细节
*/
#include "OperatorAdd.h"
#include "Operator.h"

OperatorAdd::OperatorAdd()
{
}

OperatorAdd::~OperatorAdd()
{
}

//以后随着项目越来越多,这里面代码会越来越多
double OperatorAdd::countResult()
{
    //此处略去100万行代码
    //......
    double result = getNumb0() + getNumb1();
    return result;
}


#pragma once
#include "Operator.h"
class OperatorAdd :
    public Operator
{
public:
    OperatorAdd();
    ~OperatorAdd();
    virtual double countResult();
};


#include "OperatorDiv.h"
#include "Operator.h"

OperatorDiv::OperatorDiv()
{
}

OperatorDiv::~OperatorDiv()
{
}

//以后随着项目越来越多,这里面代码会越来越多
double OperatorDiv::countResult()
{
    //此处略去100万行代码
    //......
    double result;
    if (getNumb1() != 0)
    {
        result = getNumb0() / getNumb1();
    }
    else
    {
        throw "Division by zero condition!";
    }
    return result;
}


#pragma once
#include "Operator.h"
class OperatorDiv :
    public Operator
{
public:
    OperatorDiv();
    ~OperatorDiv();
    virtual double countResult();
};


#include "OperatorMul.h"
#include "Operator.h"

OperatorMul::OperatorMul()
{
}

OperatorMul::~OperatorMul()
{
}

//以后随着项目越来越多,这里面代码会越来越多
double OperatorMul::countResult()
{
    //此处略去100万行代码
    //......
    double result = getNumb0() * getNumb1();
    return result;
}


#pragma once
#include "Operator.h"
class OperatorMul :
    public Operator
{
public:
    OperatorMul();
    ~OperatorMul();
    virtual double countResult();
};


#include "OperatorSub.h"
#include "Operator.h"

OperatorSub::OperatorSub()
{
}

OperatorSub::~OperatorSub()
{
}

//以后随着项目越来越多,这里面代码会越来越多
double OperatorSub::countResult()
{
    //此处略去100万行代码
    //......
    double result = getNumb0() - getNumb1();
    return result;
}


#pragma once
#include "Operator.h"
class OperatorSub :
    public Operator
{
public:
    OperatorSub();
    ~OperatorSub();
    virtual double countResult();
};

4 使用设计模式解决耦合问题

    在实际项目中,我们使用了设计模式中的简单工厂模式进行代码的重新架构。在此,我们为了增加代码的健壮性,做了输入之后,判断运算符的输入个数,因此做了两种模式,一种是将输入的信息转化成cha型,另外一种是将输入的信息转化成string型。String的目的是为了读取键盘中敲入了运算符的个数是多少个,如果超过一个的话,那么我们会其是他的运算符,个数超限。如果是一个的话,那么我们再进行判断,看他是不是我们本次工程中需求的四种运算符及加减乘除四种运算符,如果不是这四种运算符中的字符,我们将会报错。而在数据输入中,我们为了检验输入的数据是不是double型,我们做了进行判断,其cin.good()这样来判断字符中会不会出现其他非法字符的情况,保证运算中我们对整个运算进行了Throw监测目的是确保每次出现错误后会被cache抓到,保证cache的抓取过程中我们抓取的几种类型,一种类型是int类型,一种类型则是string类型。整个代码在main函数中暴露出来的都是高层的抽象的。具体的实现则是在它的内部实现的,给用户暴露的是相对比较简单的内容。整体代码如下:

/*
实现一个简易的计算器
版本:v1.4
时间:2022年1月21日17:04:49
开发者:董康乐
陕西西安

TODO v1.4 引出需求(计算器的设计)
使用 设计模式 简单工厂
TODO 解决耦合度高的问题

更新:try catch throw
时间:2022年1月21日21:52:24
*/

#include <iostream>
#include <sstream>
#include <iomanip> 
#include "OperatorFactory.h"

using namespace std;
int check(char buf, int len)
{
    int i;
    char tmp[1024];
    memcpy(tmp, &buf, len);
    for (i = 0; i<len; i++){
        if ((tmp[i]>0x39 || tmp[i]<0x30) && (tmp[i] != '.'))
            return 1;
    }
    return 0;
}
void main()
{
    std::cout << "第一版计算器v1.4" << std::endl;
    /*-----------------------------------------*/
    //客户端
    //定义字符和数字

    double Numb0 = 0.0;
    double Numb1 = 0.0;
    try{
        if (0)
        {
            char operatorFunction;//加减乘除四种
            std::cout << "请输入运算符:" << std::endl;
            std::cin >> operatorFunction;
            if ((operatorFunction != '+'&&operatorFunction != '-'
                &&operatorFunction != '/' && operatorFunction != '*'))
            {
                throw "运算符输入错误!";
            }

            std::cout << "请输入第一个数字:" << std::endl;
            cin >> Numb0;
            //if ()
            std::cout << "请输入第二个数字:" << std::endl;
            cin >> Numb1;
            /*-----------------------------------------*/
            //业务端//只面向抽象,面向高层 ,面向接口的感觉    //此工厂模式至面向高层 抽象
            Operator  *oper = OperatorFactory::CreatOperator(operatorFunction);
            oper->setNumb0(Numb0);
            oper->setNumb1(Numb1);
            cout << "运算结果为:" << std::to_string(oper->countResult());//输出结果
        }
        else
       {            
            string operatorFunction;//加减乘除四种

            std::cout << "请输入运算符:" << std::endl;
            std::cin >> operatorFunction;
            if ((operatorFunction.size())>1)//判断运算符个数是否过多
            {
                throw "运算符个数过多!";
            }

            if ((operatorFunction != "+"&&operatorFunction != "-"     //判断字符串是我为运算符
                &&operatorFunction != "/" && operatorFunction != "*"))
            {
                throw "运算符输入错误!";
            }

            std::cout << "请输入第一个数字:" << std::endl;
            cin >> Numb0;
            //判断输入的是否为数字(如果含有两个小数点,第二个小数点及后面的数字后默认为0)
            if (!cin.good()){
                throw "输入的第一个数字为非数字!";
            }
            std::cout << "请输入第二个数字:" << std::endl;
            cin >> Numb1;
            if (!cin.good()){
                throw "输入的第二个数字为非数字!";
            }
            /*-----------------------------------------*/
            //业务端//只面向抽象,面向高层 ,面向接口的感觉    //此工厂模式至面向高层 抽象
            Operator  *oper = OperatorFactory::CreatOperator(operatorFunction);
            oper->setNumb0(Numb0);
            oper->setNumb1(Numb1);
            cout << "运算结果为:" << std::to_string(oper->countResult());//输出结果
        }

    }
    //此处的catch监测的是运算符输入是否正确,
    //如果错误,则被判断运算符的throw监测,并输出字符串类型的异常信息,
    //这个catch就会抓住,并显示这个字符串
    catch (const char* e) {        
        cout << e << endl;
    }

    //此处的catch监测的是除法运算中分母为零的错误,
    //如果错误,则被判断分母为零的throw监测(在OperatorDiv类中监测),并输出int类型的异常信息(0),
    //这个catch就会抓住,并显示这个字符串
    catch (int e) {
        cout << "发生异常!监测到除法运算中分母为: " <<to_string(e) << endl;
    }

    //对于其他不好说的错误,则在这个catch中被检测
    catch (...) {
        cout << "发生其他未知异常!" << endl;
    }
    std::system("pause");
}

/*
关于try catch throw
一、基本理论
try用来保护特定行内的代码,确保其正确,一旦有异常,会被检测到
throw检测器所在上面的所有代码,
catch 监测异常的情况;根据不同的throw类型,来检测不同的异常;对于三不管的代码,则是catch(...)来输出
其中,一致的catch顺序只能是顺序执行,因此我们通常将catch(...)放在最后,把最后一道关
    try{

        //此处有十万行代码-------代码段1
            throw xxx(类型);//此处的throw只负责监控上面10w行代码,下面的一百万行代码2,还是“黑跑”
        //此处有一百万行代码-------代码段2
            throw yyy(类型);//此处的throw只负责监控上面10w行代码1和一百万行代码2,下面的1000w行代码不保护
        //此处有一千万行代码-------代码段3
            throw zzz(类型);
    }
    catch(xxx(类型))
    {

    }
    catch(yyy(类型))
    {

    }
    catch(zzz(类型))
    {

    }
    catch(...)//检测其他方面的
    {

    }

*/


/*
首先定义规则。【计算器业务的规则】
抽象的:numb0、numb1、计算使用 依赖导致原则
*/
#include "Operator.h"

Operator::Operator()
{
}

Operator::~Operator()
{
}

double Operator::getNumb0()
{
    return numb0;
}

double Operator::getNumb1()
{
    return numb1;
}

void Operator::setNumb0(double numb0)
{
    this->numb0 = numb0;
}
void Operator::setNumb1(double numb1)
{
    this->numb1 = numb1;
}


#pragma once
class Operator
{
public:
    Operator();
    ~Operator();
    double getNumb0();
    double getNumb1();

    void setNumb0(double);
    void setNumb1(double);

    virtual double countResult()=0;
private:
    double numb0;
    double numb1;

};


#include "OperatorFactory.h"
#include <iostream>
#include <sstream>
#include <iomanip> 
#include "Operator.h"
#include "OperatorMul.h"
#include "OperatorAdd.h"
#include "OperatorSub.h"
#include "OperatorDiv.h"
#include <iostream>
#include <sstream>
#include <iomanip> 

//前面应用了 依赖倒置原则 +  单一职责原则
//再次应用 简单工厂模式 来解决 客户端的解耦合

OperatorFactory::OperatorFactory()
{
}

OperatorFactory::~OperatorFactory()
{
}

//给用户暴露的永远是高层和抽象,而不是暴露细节,
//如果暴露工具类,那就是工具类了,就不再是我们这次的工厂模式
Operator* OperatorFactory::CreatOperator(char oper_char )
{
    Operator  *oper = NULL;
    switch (oper_char)
    {
    case '+':
        oper = new OperatorAdd();
        break;
    case '-':
        oper = new OperatorSub();
        break;
    case '*':
        oper = new OperatorMul();
        break;
    case '/':
        oper = new OperatorDiv();
        break;
    }
    return oper;
}

Operator* OperatorFactory::CreatOperator(string oper_string)
{
    Operator  *oper = NULL;
    char  oper_char = oper_string[0];
    switch (oper_char)
    {
    case '+':
        oper = new OperatorAdd();
        break;
    case '-':
        oper = new OperatorSub();
        break;
    case '*':
        oper = new OperatorMul();
        break;
    case '/':
        oper = new OperatorDiv();
        break;
    }
    return oper;
}


#pragma once
#include <iostream>
#include <sstream>
#include <iomanip> 
#include "Operator.h"
#include "OperatorMul.h"
#include "OperatorAdd.h"
#include "OperatorSub.h"
#include "OperatorDiv.h"
using namespace std;
class OperatorFactory
{
public:
    OperatorFactory();
    ~OperatorFactory();
    /*传入 一个计算器的操作符号*/
    static Operator* CreatOperator(char);
    static Operator* CreatOperator(string);

};

/*
使用 单一职责原则
加法业务的逻辑 具体细节
*/
#include "OperatorAdd.h"
#include "Operator.h"

OperatorAdd::OperatorAdd()
{
}

OperatorAdd::~OperatorAdd()
{
}

//以后随着项目越来越多,这里面代码会越来越多
double OperatorAdd::countResult()
{
    //此处略去100万行代码
    //......
    double result = getNumb0() + getNumb1();
    return result;
}


#pragma once
#include "Operator.h"
class OperatorAdd :
    public Operator
{
public:
    OperatorAdd();
    ~OperatorAdd();
    virtual double countResult();
};


#include "OperatorDiv.h"
#include "Operator.h"

OperatorDiv::OperatorDiv()
{
}

OperatorDiv::~OperatorDiv()
{
}

//以后随着项目越来越多,这里面代码会越来越多
double OperatorDiv::countResult()
{
    //此处略去100万行代码
    //......
    double result;
    if (getNumb1() != 0)
    {
        result = getNumb0() / getNumb1();
    }
    else
    {
        throw 0;//如果分母为零,则会再次被throw监测到,经这个int类型的变量甩出,并被第二个catch(int)监测
    }
    return result;
}


#pragma once
#include "Operator.h"
class OperatorDiv :
    public Operator
{
public:
    OperatorDiv();
    ~OperatorDiv();
    virtual double countResult();
};


#include "OperatorMul.h"
#include "Operator.h"

OperatorMul::OperatorMul()
{
}

OperatorMul::~OperatorMul()
{
}

//以后随着项目越来越多,这里面代码会越来越多
double OperatorMul::countResult()
{
    //此处略去100万行代码
    //......
    double result = getNumb0() * getNumb1();
    return result;
}


#pragma once
#include "Operator.h"
class OperatorMul :
    public Operator
{
public:
    OperatorMul();
    ~OperatorMul();
    virtual double countResult();
};


#include "OperatorSub.h"
#include "Operator.h"

OperatorSub::OperatorSub()
{
}

OperatorSub::~OperatorSub()
{
}

//以后随着项目越来越多,这里面代码会越来越多
double OperatorSub::countResult()
{
    //此处略去100万行代码
    //......
    double result = getNumb0() - getNumb1();
    return result;
}


#pragma once
#include "Operator.h"
class OperatorSub :
    public Operator
{
public:
    OperatorSub();
    ~OperatorSub();
    virtual double countResult();
};

参考文献

设计模式演变
try块和异常处理
try、catch语句

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

推荐阅读更多精彩内容