C++入门10 -- 友元,内部类,运算符重载

友元

  • 友元包含友元函数和友元类;
  • 若将函数A(非成员函数)声明为类C的友元函数,那么函数A就能直接访问类C对象的所有成员;
  • 若将类A声明为类C的友元类,那么类A的所有成员函数都能直接类C对象的所有成员;
  • 友元破坏了面向对象的封装性,但在某些频繁访问成员变量的地方可以提高性能;
#include <iostream>

using namespace::std;

//class Person {
//
//public:
//    //声明并初始化
//    int m_age = 0;
//    //定义虚函数
//    virtual void run(){
//
//    }
//
//    Person(int age = 0) : m_age(age){
//        cout << "Person() " << endl;
//    }
//};

class Point {
    int m_x;
    int m_y;
public:
    Point(int x,int y) : m_x(x),m_y(y){ }
    
    int getX() const {
        return this->m_x;
    }
    
    int getY() const {
        return this->m_y;
    }
    //友元函数
    friend Point add(const Point &,const Point &);
    //友元类
    friend class Math;
};

Point add(const Point &point1,const Point &point2){
    return Point(point1.m_x + point2.m_x,point1.m_y + point2.m_y);
}

class Math {
    
public:
    Point sub(const Point &point1,const Point &point2){
        return Point(point1.m_x - point2.m_x,point1.m_y - point2.m_y);
    }
};

int main(int argc, const char * argv[]) {
    
    Point p1(10,20);
    Point p2(20,30);
    
    Point p = add(p1, p2);
    
    return 0;
}

内部类

  • 将类A定义在类C的内部,那么类A就是一个内部类;
  • 内部类支持public,protected,private权限;
  • 成员函数可以直接访问其外部类对象的所有成员,反之不行;
  • 成员函数可以直接不带类名,对象名直接访问其外部类的static成员;
  • 内部类不会影响外部类的内存布局;
#include <iostream>

using namespace::std;

class Person {
    int m_age;
    
public:
    static int ms_legs;
    
    Person(){
        cout << "Person()" << endl;
    }
    //内部类Car
    class Car {
        int m_price;
    public:
        Car(){
            cout << "Car()" << endl;
        }
        void run(){
            Person person;
            person.m_age = 20;
            
            ms_legs = 4;
        }
    };
};

int Person::ms_legs = 2;


class Point {

public:
    //内部类Math
    class Math {
    public:
        void test();
    };
};

//内部类声明与实现分离
void Point::Math::test(){
    
}

int main(int argc, const char * argv[]) {

    Person person;
    //内部类
    Person::Car car;
    
    return 0;
}

局部类

  • 在一个函数内部定义的类,称之为局部类;
  • 其作用域仅局限于所在的函数内部;
  • 其所有的成员必须定义在类的内部,不允许定义static成员变量;
  • 成员函数不能直接访问函数内部的局部变量,static变量除外;
#include <iostream>

void test(){
    int age = 10;
    static int s_age = 30;
    //局部类
    class Person{
        void run(){
            s_age = 40;
            age = 20; //报错
        }
    };
}

int main(int argc, const char * argv[]) {
    
    return 0;
}

运算符重载

  • 运算符重载:可以为运算符增加一些新的功能;
  • 全局函数与成员函数都支持运算符重载;
#include <iostream>

using namespace::std;

class Point {
public:
    int m_x;
    int m_y;
    
    Point(int x,int y) : m_x(x),m_y(y){ }
    void display(){
        cout << this->m_x << "-" << this->m_y << endl;
    }
    friend Point operator+(const Point &,const Point &);
};
//运算符重载
Point operator+(const Point &p1,const Point &p2){
    return Point(p1.m_x + p2.m_x,p1.m_y + p2.m_y);
}

int main(int argc, const char * argv[]) {
    
    Point p1(10,20);
    Point p2(20,30);
   
    Point p3 = p1 + p2;
    p3.display();
    
    return 0;
}
  • operator+重载 + 运算符,实现两个point对象相加;
  • 可将operator+ 写成Point类的成员函数如下:
#include <iostream>

using namespace::std;

class Point {
public:
    int m_x;
    int m_y;
    
    Point(int x,int y) : m_x(x),m_y(y){ }
    void display(){
        cout << this->m_x << "-" << this->m_y << endl;
    }
    
    //运算符重载
    Point operator+(const Point &p){
        return Point(this->m_x + p.m_x,this->m_y + p.m_y);
    }
};

int main(int argc, const char * argv[]) {
    
    Point p1(10,20);
    Point p2(20,30);
    
    Point p3 = p1 + p2;
    p3.display();
    
    return 0;
}
  • 下面实现一个Point类关于运算符重载的封装:
//  Point.hpp
//  C++34_运算符重载
//
//  Created by ljj on 8/15/21.
//

#ifndef Point_hpp
#define Point_hpp

#include <stdio.h>
#include <iostream>

using namespace::std;

class Point {
public:
    //友元函数
    friend ostream &operator<<(ostream &,const Point &);
    
    int m_x;
    int m_y;
    
    //构造函数
    Point(int x,int y) : m_x(x),m_y(y){ }
    
    //运算符重载
    Point operator+(const Point &p);
    Point operator-(const Point &p);
    Point &operator+=(const Point &p);
    
    bool operator==(const Point &p);
    bool operator!=(const Point &p);
    
    //返回的point对象 不允许赋值
    const Point operator-() const;
    
    //前++
    Point &operator++();
    
    //后++ 返回值不可修改
    const Point operator++(int);
};

#endif /* Point_hpp */
//  Point.cpp
//  C++34_运算符重载
//
//  Created by ljj on 8/15/21.
//

#include "Point.hpp"

//运算符重载
Point Point::operator+(const Point &p){
    return Point(this->m_x + p.m_x,this->m_y + p.m_y);
}

Point Point::operator-(const Point &p){
    return Point(this->m_x - p.m_x,this->m_y - p.m_y);
}

Point &Point::operator+=(const Point &p){
    this->m_x += p.m_x;
    this->m_y += p.m_y;
    return *this;
}

bool Point::operator==(const Point &p){
    return (this->m_x == p.m_x && this->m_y == p.m_y);
}

bool Point::operator!=(const Point &p){
    return (this->m_x != p.m_x || this->m_y != p.m_y);
}

//返回的point对象 不允许赋值
const Point Point::operator-() const {
    return Point(-this->m_x,-this->m_y);
}

//前++
Point &Point::operator++(){
    this->m_x++;
    this->m_y++;
    return *this;
}

//后++ 返回值不可修改
const Point Point::operator++(int){
    Point point(this->m_x,this->m_y);
    this->m_x++;
    this->m_y++;
    return point;
}

//友元函数
ostream &operator<<(ostream &cout,const Point &point){
    return cout << point.m_x << "," << point.m_y;
}
//  main.cpp
//  C++34_运算符重载
//
//  Created by ljj on 8/14/21.
//

#include <iostream>
#include "Point.hpp"

using namespace::std;


int main(int argc, const char * argv[]) {
    
    Point p1(10,20);
    Point p2(20,30);
    
    Point p3 = p1 + p2;
    cout << p3 << endl;
    
    Point p4 = p1 - p2;
    cout << p4 << endl;
    
    ++p1;
    cout << p1 << endl;
    
    p2++;
    cout << p2 << endl;
    
    return 0;
}
运算符重载实战
  • 自定义一个String字符串类,与C++系统类string功能类似;
//  String.hpp
//  C++35_运算符重载(String)
//
//  Created by ljj on 8/15/21.
//

#ifndef String_hpp
#define String_hpp

#include <stdio.h>
#include <iostream>

using namespace::std;

class String {
    friend ostream &operator<<(ostream &,const String &);
private:
    //C语言字符串
    char *m_cstring;
public:
    //单参数构造函数
    String(const char *ctring);
    
    ~String();
    
    String &operator=(const char *cstring);
};

#endif /* String_hpp */
#include "String.hpp"

String::String(const char *cstring){
    cout << "String::String(const char *cstring)" << endl;
    if (!cstring) return;
    this->m_cstring = new char[strlen(cstring)+1]{};
    strcpy(this->m_cstring, cstring);
}

String::~String(){
    cout << "String::~String()" << endl;
    if (!this->m_cstring) return;
    delete [] this->m_cstring;
    this->m_cstring = NULL;
}

String &String::operator=(const char *cstring){
    //释放旧的字符串
    if (this->m_cstring){
        cout << "delete [] 释放旧的字符串 = " << this->m_cstring << endl;
        delete [] this->m_cstring;
        this->m_cstring = NULL;
    }
    //指向新的字符串
    if (cstring){
        this->m_cstring = new char[strlen(cstring)+1]{};
        strcpy(this->m_cstring, cstring);
        cout << "new [] 指向新的字符串 = " << cstring <<  endl;
    }
    return *this;
}

ostream &operator<<(ostream &cout,const String &string){
    if (!string.m_cstring) return cout;
    return cout << string.m_cstring;
}
第一种调用情况如下:
#include <iostream>
#include "String.hpp"

using namespace::std;

int main(int argc, const char * argv[]) {
    //直接调用构造函数
    String str1("333");
    cout << str1 << endl;
    
    //隐式调用单参数构造函数
    String str2 = "123";
    cout << str2 << endl;
    
    //隐式调用单参数构造函数
    char name[] = "444";
    String str3 = name;
    cout << str3 << endl;
    
    return 0;
}
  • 防止字符串被提前销毁释放,即String实例对象还存在,但字符串内容已经释放,需要将字符串内容拷贝到堆空间,需对构造函数析构函数作出特殊处理;
第二种调用情况:
#include <iostream>
#include "String.hpp"

using namespace::std;

int main(int argc, const char * argv[]) {
    
    //隐式调用单参数构造函数
    String str2 = "123";
    cout << str2 << endl;
    
    //重新赋值
    str2 = "666";
    cout << str2 << endl;
    
    return 0;
}
  • str2前后两次赋值,其内存操作如下:
Snip20210815_165.png
  • String str2 = "123",隐式调用单参数构造函数,创建一个String实例对象str2,且会将123字符串内容拷贝到堆区,然后m_cstring指向堆区字符串123
  • str2 = "666",其中"666"会隐式调用单参数构造函数,创建一个临时的字符串String实例对象,且会将666字符串内容拷贝到堆区,然后m_cstring指向堆区字符串666,最后将str2对象的m_cstring指针指向堆区字符串666,那么原来的堆区字符串123就没有人管理了,直接导致内存泄漏,为了防止这种情况,下面重载=运算符,在str2 = "666"赋值的时候做特殊处理,如下所示:
String &String::operator=(const char *cstring){
    //释放旧的字符串
    if (this->m_cstring){
        cout << "delete [] 释放旧的字符串 = " << this->m_cstring << endl;
        delete [] this->m_cstring;
        this->m_cstring = NULL;
    }
    //指向新的字符串
    if (cstring){
        this->m_cstring = new char[strlen(cstring)+1]{};
        strcpy(this->m_cstring, cstring);
        cout << "new [] 指向新的字符串 = " << cstring <<  endl;
    }
    return *this;
}
  • 总结一句话:释放旧的字符串,指向新的字符串;
第三种调用情况:
#include <iostream>
#include "String.hpp"

using namespace::std;

int main(int argc, const char * argv[]) {
    //隐式调用单参数构造函数
    String str1 = "111";
    String str2 = "123";
    
    str1 = str2;
    
    return 0;
}
  • str1 = str2,两者都是String字符串对象,需重载=运算符,参数为String字符串的情况;
String &String::operator=(const String &string){
    return operator=(string.m_cstring);
}
第四种调用情况:
#include <iostream>
#include "String.hpp"

using namespace::std;

int main(int argc, const char * argv[]) {
    //隐式调用单参数构造函数
    String str1 = "111";
    String str2 = "123";
    
    str1 = str1;
    
    return 0;
}
  • str1 = str1,赋值操作用的同一个String对象,会调用String &String::operator=(const String &string)函数,接着会调用String &String::operator=(const char *cstring)函数,其内存操作如下:
Snip20210815_166.png
  • 接下来执行,释放旧的字符串,指向新的字符串,str1对象的m_cstring指针清空,并且堆空间的111字符串释放掉,但参数const char *cstring指针依然指向堆空间的111字符串,出现野指针错误,所以要针对 用字符串本身赋值时要做判断处理;
String &String::operator=(const char *cstring){
    //指向相同的堆空间 直接返回
    if (this->m_cstring == cstring) return *this;
    //释放旧的字符串
    if (this->m_cstring){
        cout << "delete [] 释放旧的字符串 = " << this->m_cstring << endl;
        delete [] this->m_cstring;
        this->m_cstring = NULL;
    }
    //指向新的字符串
    if (cstring){
        this->m_cstring = new char[strlen(cstring)+1]{};
        strcpy(this->m_cstring, cstring);
        cout << "new [] 指向新的字符串 = " << cstring <<  endl;
    }
    return *this;
}
第五种调用情况:
#include <iostream>
#include "String.hpp"

using namespace::std;

int main(int argc, const char * argv[]) {
    //隐式调用单参数构造函数
    String str1 = "111";
    
    //拷贝构造函数 默认浅拷贝
    String str2 = str1;
    
    return 0;
}
  • 涉及拷贝构造函数,默认是浅拷贝,需要进行深拷贝;
String::String(const String &string){
//    this->m_cstring = string.m_cstring;
    operator=(string.m_cstring);
//    *this = string.m_cstring;
}
  • String类的完整代码如下:
//  String.hpp
//  C++35_运算符重载(String)
//
//  Created by ljj on 8/15/21.
//

#ifndef String_hpp
#define String_hpp

#include <stdio.h>
#include <iostream>

using namespace::std;

class String {
    friend ostream &operator<<(ostream &,const String &);
private:
    //C语言字符串
    char *m_cstring;
    
public:
    //单参数构造函数
    String(const char *cstring);
    
    //拷贝构造函数
    String(const String &string);
    
    ~String();
    
    //C语言字符串
    String &operator=(const char *cstring);
    //String字符串
    String &operator=(const String &string);
};


#endif /* String_hpp */
//  String.cpp
//  C++35_运算符重载(String)
//
//  Created by ljj on 8/15/21.
//

#include "String.hpp"

String::String(const char *cstring){
    if (!cstring) return;
    cout << "String::String(const char *cstring) -- new " << cstring << endl;
    this->m_cstring = new char[strlen(cstring)+1]{};
    strcpy(this->m_cstring, cstring);
}

String::String(const String &string){
//    this->m_cstring = string.m_cstring;
    operator=(string.m_cstring);
//    *this = string.m_cstring;
}

String::~String(){
//    if (!this->m_cstring) return;
//    cout << "String::~String() delete" << this->m_cstring << endl;
//    delete [] this->m_cstring;
//    this->m_cstring = NULL;
    
    operator=(NULL);
    //将NULL赋值给当前对象 等价写法
//    *this = NULL;
//    (*this).operator=(NULL);
//    this->operator=(NULL);
}

String &String::operator=(const char *cstring){
    //指向相同的堆空间 直接返回
    if (this->m_cstring == cstring) return *this;
    //释放旧的字符串
    if (this->m_cstring){
        cout << "delete [] 释放旧的字符串 = " << this->m_cstring << endl;
        delete [] this->m_cstring;
        this->m_cstring = NULL;
    }
    //指向新的字符串
    if (cstring){
        this->m_cstring = new char[strlen(cstring)+1]{};
        strcpy(this->m_cstring, cstring);
        cout << "new [] 指向新的字符串 = " << cstring <<  endl;
    }
    return *this;
}

String &String::operator=(const String &string){
//    return operator=(string.m_cstring);
    return *this = string.m_cstring;
}

ostream &operator<<(ostream &cout,const String &string){
    if (!string.m_cstring) return cout;
    return cout << string.m_cstring;
}
  • 将字符串的判断,释放旧字符串,创建新堆空间存放字符串全部封装到一个函数方法assign中,进行封装改造:
#ifndef String_hpp
#define String_hpp

#include <stdio.h>
#include <iostream>

using namespace::std;

class String {
    friend ostream &operator<<(ostream &,const String &);
private:
    //C语言字符串
    char *m_cstring;
    
    String &assign(const char *cstring);
    
public:
    //单参数构造函数
    String(const char *cstring);
    
    //拷贝构造函数
    String(const String &string);
    
    ~String();
    
    //C语言字符串
    String &operator=(const char *cstring);
    //String字符串
    String &operator=(const String &string);
};

#endif /* String_hpp */
//  String.cpp
//  C++35_运算符重载(String)
//
//  Created by ljj on 8/15/21.
//

#include "String.hpp"

String::String(const char *cstring){
    assign(cstring);
}

String::String(const String &string){
    assign(string.m_cstring);
}

String::~String(){
    assign(NULL);
}

String &String::operator=(const String &string){
    return assign(string.m_cstring);
}

String &String::assign(const char *cstring){
    //指向相同的堆空间 直接返回
    if (this->m_cstring == cstring) return *this;
    //释放旧的字符串
    if (this->m_cstring){
        cout << "delete [] 释放旧的字符串 = " << this->m_cstring << endl;
        delete [] this->m_cstring;
        this->m_cstring = NULL;
    }
    //指向新的字符串
    if (cstring){
        this->m_cstring = new char[strlen(cstring)+1]{};
        strcpy(this->m_cstring, cstring);
        cout << "new [] 指向新的字符串 = " << cstring <<  endl;
    }
    return *this;
}

ostream &operator<<(ostream &cout,const String &string){
    if (!string.m_cstring) return cout;
    return cout << string.m_cstring;
}
  • String的完整封装,功能如下:
//  String.hpp
//  C++35_运算符重载(String)
//
//  Created by ljj on 8/15/21.
//

#ifndef String_hpp
#define String_hpp

#include <stdio.h>
#include <iostream>

using namespace::std;

class String {
    friend ostream &operator<<(ostream &,const String &);
private:
    //C语言字符串
    char *m_cstring = NULL;
    
    String &assign(const char *cstring);
    //C语言字符串拼接
    char* join(const char *cstring1,const char *cstring2);
    
public:
    //单参数构造函数 默认参数值为空
    String(const char *cstring = "");
    
    //拷贝构造函数
    String(const String &string);
    
    ~String();
    
    //C语言字符串
    String &operator=(const char *cstring);
    //String字符串
    String &operator=(const String &string);
    
    String operator+(const char *cstring);
    String operator+(const String &string);
    
    String &operator+=(const char *cstring);
    String &operator+=(const String &string);
    
    //返回指定位置的字符
    char operator[](size_t index);
    
    bool operator>(const char *cstring);
    bool operator>(const String &string);
};


#endif /* String_hpp */
//  String.cpp
//  C++35_运算符重载(String)
//
//  Created by ljj on 8/15/21.
//

#include "String.hpp"

String::String(const char *cstring){
    assign(cstring);
}

String::String(const String &string){
    assign(string.m_cstring);
}

String::~String(){
    assign(NULL);
}

String &String::operator=(const String &string){
    return assign(string.m_cstring);
}

String &String::assign(const char *cstring){
    //指向相同的堆空间 直接返回
    if (this->m_cstring == cstring) return *this;
    //释放旧的字符串
    if (this->m_cstring){
        cout << "delete [] 释放旧的字符串 = " << this->m_cstring << endl;
        delete [] this->m_cstring;
        this->m_cstring = NULL;
    }
    //指向新的字符串
    if (cstring){
        this->m_cstring = new char[strlen(cstring)+1]{};
        strcpy(this->m_cstring, cstring);
        cout << "new [] 指向新的字符串 = " << cstring <<  endl;
    }
    return *this;
}

char* String::join(const char *cstring1,const char *cstring2){
    if (!cstring1 || !cstring2) return NULL;
    
    char *newString = new char[strlen(cstring1) + strlen(cstring2) + 1]{};
    strcat(newString, cstring1);
    strcat(newString, cstring2);
    return newString;
}


String String::operator+(const char *cstring){
    String str;
    char *newString = join(this->m_cstring, cstring);
    if (newString){
        //回收str 之前的空的("")堆空间
        str.assign(NULL);
        
        str.m_cstring = newString;
    }
    return str;
}

String String::operator+(const String &string){
    return operator+(string.m_cstring);
}

String &String::operator+=(const char *cstring){
    char *newString = join(this->m_cstring, cstring);
    if (newString) {
        this->assign(NULL);
        this->m_cstring = newString;
    }
    //返回当前对象本身 给返回值引用
    return *this;
}

String &String::operator+=(const String &string){
    return operator+=(string.m_cstring);
}

char String::operator[](size_t index){
    if (index < 0 || !this->m_cstring) return '\0';
    if (index >= strlen(this->m_cstring)) return '\0';
    return this->m_cstring[index];
}

bool String::operator>(const char *cstring){
    if (!this->m_cstring || !cstring) return 0;
    return strcmp(this->m_cstring, cstring) > 0;
}

bool String::operator>(const String &string){
    return operator>(string.m_cstring);
}

ostream &operator<<(ostream &cout,const String &string){
    if (!string.m_cstring) return cout;
    return cout << string.m_cstring;
}

调用父类的运算符重载函数

//  main.cpp
//  C++35_运算符重载(String)
//
//  Created by ljj on 8/15/21.
//

#include <iostream>

using namespace::std;

class Person {
    int m_age;
public:
    Person &operator=(const Person &person){
        this->m_age = person.m_age;
        return *this;
    }
};

class Student : public Person {
    int m_score;
    
public:
    Student &operator=(const Student &student){
        //调用父类的运算符重载函数
        Person::operator=(student);
        this->m_score = student.m_score;
        return *this;
    }
};

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

推荐阅读更多精彩内容