C++ primer plus V6 泛读知识点

2018年8月17日 22:14:16


title: C++PrimerPlus读书笔记
typora-root-url: ..
typora-copy-images-to: ..\img\root
date: 2018-08-15 22:55:29
tags:
c++
学习
categories:
学习


C++PrimerPlus读书笔记

第一章

  • C++融合了C的面向过程,添加了以类为代表的面向对象,模板支持的泛型编程.
  • C是一种结构化编程的语言.强调算法.C++ 强调的是一种数据结构
  • 扩展名大小写,c是标准的c,C是支持C++的C

第二章

  • end1\n相比,都可以执行换行,但是会刷新输出缓冲.
  • token是代码中不可分割的元素
  • 声明包含了定义声明和引用声明两种形式
  • cin中的<<是进行了运算符重载,所以他能实现各种格式输出
  • linux使用命令链接函数库g++main.c -lm
  • using namespace 放在函数中,则只对函数起作用,放在函数外,则对所有函数有用

第三章

  • 内置类型分为基本类型(整数和浮点数)和复合类型(数组,字符串,指针,结构)
  • 以两个下划线或者一个下划线和大写字母打头的名称被保留,以及一个下划线打头的用作全局标识.P38
  • short至少16位,int至少与short一样长,long至少32位,至少=int,long long 至少64位,至少=long,在limits中限制
  • 变量初始化方式
    • c++ 可以使用类似int i(60);初始化变量,这类似将int看作一个class,60为构造函数的参数?
    • 也可使用{},如果括号为空则为0,可以 int enum{7},也可以 int a={2}.
  • 数字后面的后缀 u/U表述无符号,l/L标识long
  • cinput()可以输出char的字符
  • 通用字符名以\u或者\U打头,\u后面是8个十六进制位,\U后面是16个十六进制位,用来扩展编码,
  • wcha_t可以存储汉字和日文,长度由编译器确定,是一种整数类型,不能使用cin/cout,可以使用wcin,wcout.
  • char16_t(u作为前缀)和char32_t(U作为前缀)也是用来标识字符
  • C中一般将0解释为false
  • ==const==在C和C++中有所不同,一个是作用域规则,一个是const可以声明数组长度
  • 可以使用E来标识浮点数,标识10的多少次方,d.dddE+n是将小数点右移,~n是左移
  • 浮点类型有float,double,long double.float至少32位,double至少48位且不少于float,long double至少=double,可以从float.h看到限制,c++在cfloat
  • 使用cout.setf可以设置显示精度
  • 浮点常量默认使用double存储,可以用后缀f,F指定float,用L,l指定Longdouble
  • ==float只能保证6位有效精度==
  • 优先级,附录D.先判断优先级分组,再判断结结合性,所以同一优先级的结合性是一样的,从左往右的结合性说明对于操作数,先运行左侧的运算符.P60
    • 注意:20*5+30*6,是先计算左边的20*5还是右边的30*6主要靠编译器的实现.虽然*的结合性是从左到右,但是并不是作用在同一个操作数
  • 简单的运算符重载:
    • 9/5 int除法
    • 9.0/5.0 double
    • 9L/5L long触发
  • 潜在的类型转换p64
    • 赋值时的隐式转换
    • 表达式中有不同的类型
      • short提升到int..这个在c里面应该没有
      • 不同类型,小类型转换为大类型
    • 参数传递
  • 强制类型转换
    • 形如 (int)tepm
    • c++新增 int(temp),这样int看着就是一个函数风格
    • c++还有强制类型转换运算符号4种P65
      • static_cast <long> (temp)

第四章

  • 初始化一个数组为0,只需要显示的初始化第一个元素为{0}
  • c++中有以下新的特性
    • int a[4]{1,2,3,4};
    • 在{}可以不写值,都为0
    • 列表初始化可以禁止一些缩窄的转换 比如float到int
  • 数组的替代复杂用法:模版类vector和模版类array
  • cin默认读取到空白,可以使用getline和get读行,使用getline会丢弃下一个换行,get并不会,
    • cin.clear
  • string类的简单用法
    • 允许直接相互赋值
    • 允许使用+
  • 支持其他编码格式的字符串
  • 支持RAW的字符串,里面就没有转义了,以R开头,形如R"(xxxxxxxxxxxxxx)",允许在R"(加上xxx,这样之后,尾巴也要使用)"加入一样的,形如R"xxx(YYYY)xxx",中间的YYY是实际的
  • 可以将RAW与w_char等结合,如Ru,UR暂时放着
  • 在定义变量的时候可以省略struct,与c的结构相比,允许放置成员函数,同样支持位域
//声明结构类型
struct ab{
int a;int b;
}
//c这样声明变量
struct ab tmp1;
//c++  可以省略struct
ab tmp2;
//还有以下用法

struct structName
{
    int a ; int b;
}tmpname;
当有tmpname的时候可以省略structName,这样只能用一次,这时候还可以直接初始化.
struct 
{
    int a ; int b;
}tmpname={0,1}

//还可以这么赋值,c99以后支持的,韦东山有讲
tmpname={.b=xxx;.a=xxx}
  • 公用体支持匿名公用体,这样可以少写这个公用体的名字

#include <stdio.h>
 
struct person
{
    char    *name;
    union
    {
        char gender;
        int  id;
    };
    int      age;
};
 
int main(void)
{
    struct person jim = {"jim", 'F', 28};
 
    printf("jim.gender = %c, jim.id = %d\n", jim.gender, jim.id);
 
    return 0;
}
  • 如果只是为了使用枚举常量,不声明枚举变量,可以忽略枚举名称,也就是匿名枚举.c++中的枚举默认是int,但有>int的,则使用4字节long,这有个词==作用域内枚举==
  • int * a, b; b是int.不是指针
  • 使用new为指针分配变量 int * pt = new int;//pt是个指针,new是从堆heap和自由存储区中分配内存,定义变量是在栈.(单片机的有点不一样)
  • delete释放内存 int* pt=new int;delete pt;不能使用delete释放释放过的内存,结果未知,也不能释放定义变量的内存.delete 对应的是地址,感觉应该是那个地址包含了长度,类型的链表, 可以delete 同样地址的指针,int* pt2;pt2=pt;delete pt2;
  • 数组名和指针的区别:1 数组名是常亮,不能执行运算 2. sizeof指针=4 32位系统
  • ==数组名取地址==,对于一个数组 short tell[10];,存在以下三个地址
    • &tell[0]=数组的地址
    • tell=&tell[0]=数组的地址
    • &tell,这个是一个二级指针了,他指向了tell,也就是*(&tell)=tell,指向了包含20个元素的数组
      • []优先级高于*,所以我们定义一个指针指向了一个20元素的数组这么定义 short ( *p)[20]=&tell
short tell[10];
cout<<tell<<endl;
cout<<&tell<<endl;
//从地址上看两者是相等的,但是&tell[0]=tell是一个2字节的内存地址,&tell是一个20字节的地址,所以tell+1是地址加2,&tell+1是加了20,
//可以这么来一个指针 
short (*p) [20]=&tell;
  • 结构体指针成员的获取,a->b 或者 (*a).b
  • vector 模版类,使用vector <int> vi; vector <char> ch(10);
  • 模版数组array,与模版类不同,他是用栈的 ,但是也支持new和delete array<int,5> ai;与模版类不同,他创建的对象大小是固定值,不能是变量
  • 数组,模版类,模版array都可以使用数组下标访问,诸如越界a[-2]默认情况也是允许的跨界了,如果要检查可以使用模版类/模版数组的成员函数at,形如a.at(1)=a[1]模版类是动态数组的替代,模版类array是静态数组的替代.模版数组支持直接复制.

第五章

  • 在c中,表达式成立为1,false为0,但在C++引入bool后,关系表达式是来判断bool的,C++在需要整数的时候将true和false转换为1,0,在需要bool值的时候0为false,非0为true
  • 设置显示格式:cout.setf(ios_base::boolalpha);设置为显示bool还是true
  • 表达式是没有分号的,有些表达式会对内存变量等有影响,有些没有,加上;就是语句了,所以可能会有a+3;这种没有意思的语句.
  • 对于在for内部的int变量,新的标准是在==内部有效==,老的标准是将定义置于for之前for(int a; a<5;a++)
  • using声明与using编译指令 Using std::coutUsing std
  • 顺序点与执行顺序的问题,在循环语句中,一个表达式结束才会进入另外一个语句,表达式结束意味其中的++等操作已经实现了其副作用,但是类似 y=(1+a++)+(6+a++),没有规定左右哪个先执行
  • 对于类而言,前缀++效率更高
  • 优先级浅析*++p 前缀递增与*优先级相同,从右往左,所以先结合右 ++pt,再*(++pt)
  • x=*pt++,后缀优先级更高,所以先 pt++,但是后缀运算意味着对原来的地址操作也就是x=*pt
  • 逗号运算符表达式,返回右侧的值
  • for允许使用类似for int i : iarrar[10]或者for int &i : iarrar[10]来修改数组
  • C++改变参数的值可以不使用指针,只需要将参数声明为引用istream& cin.get(char& var);,这么用cin.get(a)就能改变A的值,关于引用能改变参数值,其实就是传递地址了

第六章

  • 文件操作

  • cin输入P187

    https://blog.csdn.net/K346K346/article/details/48213811

    cin输入流的机制是:

    当遇到无效字符或遇到文件结束符(不是换行符,是文件中的数据已读完)时,输入流cin就处于出错状态,即无法正常提取数据。此时对cin流的所有提取操作将终止。在 IBM PC及其兼容机中,以Ctrl + Z表示文件结束符。在UNIX和Macintosh系统中,以 Ctrl + D表示文件结束符。当输入流cin处于出错状态时,如果测试cin的值,可以发现它的值为false(假),即cin为0值。如果输入流在正常状态,cin的值为true(真),即cin为 一个非0值。可以通过测试cin的值,判断流对象是否处于正常状态和提取操作是否成功。如:
    if(!cn) //if表达式判断为真时cin输入流处于出错状态,无法正常提取数据
    cout<<"error";

    错误处理

    • 重置cin以接受新的输入
    • 删除错误的输入cin.clear()是用来更改cin的状态标示符的。通俗的讲就是将一个出错的“流”恢复为“好”,并设置goodbit状态位。
    • 提示输入
    #include<iostream>
    const int Asize = 5;
    int main()
    {
        using namespace std;
        int golf[Asize];
        int i,sum = 0;
        float ave;
        for(i = 0;i < Asize;i++)
        {
            cout << "round # " << i + 1;
            while(!(cin >> golf[i]))
            {
                cin.clear();
                while(cin.get() != '\n')
                {
                    continue;
                }
                cout << "Please enter a number:";
            }
        }
        for(i = 0;i < Asize;i++)
        {
            sum = sum + golf[i];
        }
        ave = (float)sum / Asize;
        cout << ave << endl;
        return 0;
    }
    
    
    1. cin>> ,当cin>>从缓冲区中读取数据时,若缓冲区中第一个字符是空格、tab或换行这些分隔符时,cin>>会将其忽略并清除,继续读取下一个字符,若缓冲区为空,则继续等待。但是如果读取成功,字符后面的分隔符是残留在缓冲区的,cin>>不做处理。
    2. cin.get 关于cin.get()和cin.get(ch)区别P159
    int cin.get();
    istream& cin.get(char& var);
    istream& get ( char* s, streamsize n );
    istream& get ( char* s,  streamsize  n, char delim )。
    
    • cin.get读取一个字符 cin.get或者cin.get(var),cin.get()从输入缓冲区读取单个字符时不忽略分隔符.cin.get()的返回值是int类型,成功:读取字符的ASCII码值,遇到文件结束符时,返回EOF,即-1,Windows下标准输入输入文件结束符为Ctrl+z,Linux为Ctrl+d。cin.get(char var)如果成功返回的是cin对象,因此可以支持链式操作,如cin.get(b).get(c)。
    • cin.get读取一行,读取一行可以使用istream& get ( char* s, streamsize n )或者istream& get ( char* s, size_t n, streamsize delim )。二者的区别是前者默认以换行符结束,后者可指定结束符。n表示目标空间的大小。
      • cin.get(array,20);读取一行时,遇到换行符时结束读取,但是不对换行符进行处理,换行符仍然残留在输入缓冲区。这也是cin.get()读取一行与使用getline读取一行的区别所在.cin.get(str,size);读取一行时,只能将字符串读入C风格的字符串中,即char*,但是C++的getline函数可以将字符串读入C++风格的字符串中,即string类型。鉴于getline较cin.get()的这两种优点,建议使用getline进行行的读取。
    • cin.getline,cin.getline不会将结束符或者换行符残留在输入缓冲区中。
    istream& getline(char* s, streamsize count); //默认以换行符结束
    istream& getline(char* s, streamsize count, char delim);
    
    • exit()是定义在cstdlib中的,exit(EXIT_FAILURE),中间的EXIT_FAILURE是系统通信参数

    • ==文件操作==ifsream 和 ofstream,fstream

      • 判断是否成功打开 file.is_open

      • file.good能够判断错误,输入正确,并且没有EOF

      • 读取的时候不能超过EOP,如果遇到EOF,方法eof返true

        while(inflie.good())
        {
          infole>>value;
        }
        //还可以精简如下p197
        while(infole>>value)
        {
          //do
        }
        
        

第七章

  • 二维数组作为参数传递示例
int data[3][4]={{1,2,3,4},{2,2,3,4},{3,2,3,4}};
int total=sum{data,3}

//原型
int sum(int (*pt)[4],int size)//这里(*pt)要有括号,否则就是 pt[4] 然后是个int 指针,是个指针数组了
//或者
int sum(int pt[][4],int size)
  • 局部指针不能返回,c++中使用new分配内存是允许的,可以在外部删除
  • 结构体名字知识结构的名称,需要&取址,早期的c不允许直接按值传递结构只能传递地址,c++允许传值,可以用指针,也可以使用引用
  • 结构体允许直接复制,里面的指针是浅复制
  • 模版类,数组模版传递指针作为参数void fill (std::array<double,3> *pa)
  • 递归,在递归中,每一层的局部变量都有字节的地址存储单元.
  • 函数指针,标识这是一个指针,指向了一个函数double (*pt)(int),pt是个指针,指向一个函数,一般情况下声明一个函数指针,可以先写出这个函数的原型,再将名字换成(pt),在这里(*pt)等同与函数名,可以直接使用(pt)(参数);
  • 指针函数,标识这是一个返回值是指针的函数double * pt(int),因为括号优先级高,所以先和pt结合,说明这是函数,返回值为double *
  • 在函数中,允许省略标识符,下面三个是一样的
f1(const double ar[],int n)
f2(const double [],int )
f3(const double *,int )  
  • 函数指针数组const double * (*pa[3])(const*,int)={f1,f2,f3};
      1. pa是个数组,他的类型是*,也就是一个指向指针的数组
      2. 指针的类型是 const double * xxx(const double*,int),也就是说指针是个函数 xxx(const double *,int),参数是(const double* ,int),返回值是 const double *
  • auto 只能用于单值初始化,不能用于初始化列表,这个不太理解 P245,应该是 auto b=a; a是已知的一个值.
///都忽略参数

//step1
//pt是个指针,指向一个函数,
double (*pt)(int)
//pt是个函数,返回一个 double*
double * pt(int) 
    
//step2
// pt是个函数,返回值为 double *
const double * pt(const double *,int)
//pt是个指针,指向一个返回值为 double*的函数
const double * (*pt)(const double* ,int)
    
//step3
//[]优先级高于*,所以pt是个数组,类型为*,也就是是个指针的数组,数组里存的是指针,指向了一个函数
const double * (*pt[3]) (const double* ,int)
    
//step4
//构造一个指针,指向step3,简单的理解就是将pt改为(*pt1)就ok,pt1指向step3的数组
//1.pt是个指针  (*pt)
//2.指向一个数组 (*pt)[3]
//3.该数组的类型是个指针,我们定义一个数组,数组的元素类型为 int* 的话,这么定义  int* a[3];
    //同上,    (type)* (*pt)[3] 也就是↓
const double * (*(*pt)[3]) (double * ,int)
  • const从右往左读.... 列入const int *p 就是p is a pointer to a int which is constant...其它的都可以那么读...

第八章

  • 宏在某些情况下会有问题,不能传递参数,比如参数是(i++)这样的话使用替换可能导致内部有多次i++
  • 在创建引用变量中int & b=a;注意这里的&不是取址,而是类型标识的一部分,应认为是指向int变量的引用
  • 引用与指针的一个区别,必须在声明引用的时候初始化.更像 const指针
  • 早期的编译器允许引用是个常量,或者类型不匹配,为其创造一个临时引用,创建临时变量不会改变原值(类型不匹配的话)P263
    • 实参类型正确,但不是左值,比如常量
    • 实参类型不正确,但可以转换,比如一个long的范围比较小,转换为int,这种情况下一般需要加个const标注以下,因为不会改变引用值
  • ==引用中的匿名变量==...就是上面的临时变量
  • 可使用右值引用,使用&&
  • 如果不希望更改引用的值,使用const声明原型
  • 当函数返回值为引用,然后 a=fun(...),这样避免fun返回值先创造一个局部变量,可以直接复制.函数返回引用实际就是返回引用变量的别名 .注意:返回的引用不应该是个局部变量,就像指针一样
  • 基类的引用可以指向派生类的对象,这也就是说函数原型参数为基类,但你可以用派生类的对象
  • cout.setf() 格式化输出,返回的是所有配置,所以可以用返回值保存当前的配置P273
  • 关于使用指针或者引用:类使用引用,结构使用引用和指针,其他使用指针
  • 默认参数的右边必须是默认参数,调用是不允许跳过,比如fn(int a ,int b=1,int c=2),不能使用fn(1,,3),默认参数一般放在原型而不是实际定义中
  • 函数重载中有些参数是不能共存的,比如 x 与 &x
  • 函数模版,也就是参数的类型可以是尚未可知的,比如有一个 fn(int a) 和 fn(double a),如果内部执行步骤一致,以前我们需要写两个函数,现在可以把这个提取出来.
//下面都可以,class是老的
template <typename Anytype>//一般写作 template <typename T>
template <class Anytype>
void Swap(Antype &a, Anytype &b)
{
    Anytype temp;
    temp=b;
    b=a;
    a=temp;
}
  • 函数模板支持重载,与一般的重载函数一样,特征标需要不一样
  • 模板的局限性:
    • 一些操作无法执行,比如如果变量是个结构,那么运算符>;如果是数组,那=就不成立,等等..
    • 解决办法:1运算符重载 2 具体化模板定义
  • 对于一个函数名,依次是非模板函数>显示具体化模板>模板函数 包含他们的重载函数
  • 具体显示化格式:原型和定义以template<>打头,通过名称指出类型,显示化声明后必须要有具体的定义
//模板
template<typename T>
void Swap(T &a, T &b);

struct job
{
    int a;
    int b;
}

//显示化声明,这里的Swap(Job)中的job是可选的,参数里面已经有了
//可以简写 template<> void Swap(job &j1, Job & j2);
template<> void Swap(Job)(job &j1, Job & j2);

//显示化定义
template<> void Swap(Job)(job &j1, Job & j2)
{
    .....
}

  • 还可以在函数使用的时候创建显示实例化cout<<Add(double)(x,m)<<endlP288

  • 具体到调用函数才会产生模板的实例,称为一个实例.C++支持

    • 隐式化实例
    • 显示化实例,这种实例需要这么声明template<> void Swap(Job)(job &j1, Job & j2),或者template<> void Swap(job &j1, Job & j2);,还支持函数中直接实例化例如cout<<Add(double)(x,m)<<endl
    • 显示具体化是指针对特殊的一些结构,做出不同的实例化的函数
  • 显示实例化话是模板的一个具体实例,因为模板生成函数一般是隐式实例化的,根据实参的类型生成函数。而显示实例化直接指定生成模板的哪一种实例。显示具体化是指模板的特殊行为,理论上模板接受不同类型的参数都会按照模板定义的那样去执行,显示具体化允许在特定的形参下重新定义函数的行为。

  • 模板函数的选择有一套复杂的规则,当完全匹配后,选择那个T最简单的方式

  • 模板是支持多个参数不定的template <class T1, class T2>

  • decltype 解决一些类型未知不定的情况P295,配合auto可以声明返回值的类型,注意decltype 中如果表达式式个左值,有两个括号的是引用,一个的是值的类型

  • 所谓的特征标,就是函数的参数列表,翻译想出的高大上的傻逼词语

第九章

  • 多文件编译,头文件包含
  • 作用域表示这个东西在文件内多大范围可见,链接性表示外部文件的共享
  • 静态变量都会被先初始化,然后在初始化为其他值 bss
  • cv限定符...翻译出来的新玩意..就是const和volatile
  • mutable,表示即使结构或者类变量为const,但是限定为mutable的某个变量也可以被修改
  • const 在c++中,会将全局变量限定为static,这么做的原因是可以将const int a =2这种形式放到.h中,反复包含.当然也可以强制更改这个属性,在const前加上extern .c中没有这个说法.

todo

==数组名取地址==

for里面的作用域

备忘:

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

推荐阅读更多精彩内容