C++语言基础(持续更新)

绪论

函数定义:

返回类型 函数名(形参列表)
函数体//以左花括号开始右花括号结束的语句块

大多数系统中,main的返回值被用来指示状态,返回值0表明成功,非0的返回值的含义由系统定义,通常用来指出错误类型

cerr用来输出警告和错误信息,clog用来输出程序运行时的一般性信息

endl效果是结束当前行,并将与设备相关联的缓冲区(buffer)中的内容刷到设备中。被称为操纵符。缓冲刷新操作可以保证到目前为止程序所产生的所有输出都真正写入输出流,而不是仅停留在内存中等待写入流一直刷新流,有助于判断程序崩溃的位置

命名空间可以帮助我们避免不经意的名字定义冲突,以及使用库中相同名字造成的冲突

注释分为单行注释(//)和界定符对注释(/*和*/),其中注释界定符不能嵌套

当遇到文件结束符(end-of -file),或遇到一个无效输入时,istream对象的状态会变为无效。处于无效状态的istream对象会使条件变为假。

使用标准库时,用尖括号(<>)包围头文件名,对于不属于标准库的头文件,则用双引号("")包围。

item1.isbn()
//使用点运算符(.)来表达我们需要“名为item1的对象的isbn成员”。使用调用运算符(())来调用一个函数。调用运算符是一对圆括号,里面放置实参列表(可能为空)。

大多数编程语言通过两种方式来进一步补充其基本特征:一是赋予程序员自定义数据类型的权利,从而实现对语言的扩展;二是将一些有用的功能封装成库函数提供给程序员。

C++是一种静态数据类型语言,它的类型检查发生在编译时,因此编译器必须知道程序中每一个变量的数据类型。

变量和基本类型

算数类型包括整型(包括字符和布尔类型在内)和浮点型

算术类型
在这里插入图片描述
在这里插入图片描述

十进制字面值的类型是Int、long和long long中尺寸最小的那个。八进制和十六进制字面值的类型是int、unsigned int、long、unsigned long、long long和unsigned long long中尺寸最小的那个。(前提是能容纳其数值)

字符串字面值的类型实际上是由常量字符构成的数组。编译器在每个字符串的结尾处添加一个空字符(‘\0’),因此,字符串字面值的实际长度要比它的内容多1。

如果两个字符串字面值位置紧邻且仅有空格、缩进和换行符分隔,则它们实际上是一个整体。当书写的字符串字面值比较长,写在一行里不太合适时,就可以采取分开书写的方式。

需要使用转义字符的情况:
1.不可打印的字符,如退格或其他控制字符,因为它们没有可视的图符
2.C++语言中有特殊含义的字符(单引号、双引号、问号,反斜线)

string是一种库类型,表示可变长的字符序列

对象是指一块能存储数据并具有某种类型的内存空间

初始化不是赋值,初始化的含义是创建变量时赋予其一个初始值,而赋值的含义是把对象的当前值擦除,而以一个新值代替。

作为C++新标准的一部分,用花括号来初始化变量得到了全面的应用,这种初始化的形式被称为列表初始化。当用于内置类型的变量时,这种初始化形式有一个重要的特点:如果我们使用列表初始化且初始值存在丢失信息的风险,则编译器将报错

如果定义变量时没有指定初值,则变量将会被默认初始化,默认值由变量类型决定,同时定义变量的位置也会对此有影响。

如果内置类型的变量未被显式初始化,它的值由定义的位置决定。定义于任意函数体之外的变量被初始化为0.定义在函数体内部的内置类型将不被初始化。

每个类各自决定其初始化对象的方式。而且,是否允许不经初始化就定义对象也由类自己决定。

声明与定义
声明使得名字为程序所知,一个文件如果想使用别处定义的名字则必须包含对那个名字的声明。
定义负责创建与名字相关的实体。
相同点:都规定了变量的类型和名字
不同点:定义还申请存储空间,也可能会为变量赋一个初始值。
如果想声明一个变量而非定义它,就在变量名前添加关键字extern,而且不要显式的初始化变量

extern int i; //声明i而非定义i
int j;        //声明并定义j

任何包含了显式初始化的声明即成为定义
在函数体内部,如果试图初始化一个由extern关键字标记的变量,将引发错误
变量能且只能被定义一次,但可以被多次声明

如果要在多个文件中使用同一个变量,就必须将声明和定义分离。此时变量的定义必须出现在一个文件中,而其他用到该变量的文件必须对其进行声明,却绝对不能重复定义

标识符由字母、数字、下划线组成,其中必须以字母或下划线开头。对长度没有限制,但对大小写字母敏感。用户自定义的标识符不能连续出现两个下划线,也不能以下划线紧连大写字母开头。此外,定义在函数体外的标识符不能以下划线开头。

在这里插入图片描述
作用域是程序的一部分,在其中名字有其特定的含义。
名字的有效区域始于名字的声明语句,以声明语句所在的作用域末端为结束。

C++11新增了一种引用——右值引用,后面将会详细介绍。

引用(reference)为对象起了另一个名字,引用类型引用另外一种类型。通过将声明符写成&d的形式来定义引用类型,其中d是声明的变量名:

int ival = 1024;
int &refVal = ival;    //refVal指向ival(是ival的另一个名字)
int &refVal2;          //引用必须被初始化

*定义引用时,程序把引用和它的初始值绑定(bind)在一起,而不是将初始值拷贝给引用。一旦初始化完成,引用将和它的初始值一直绑定在一起。因为无法令引用重新绑定到另外一个对象,因此引用必须初始化。

定义一个引用之后对其进行的所有操作都是在与之绑定的对象上进行的:

refVal = 2;       //把2赋给refVal指向的对象,此处赋给了ival
int ii = refVal;  //与ii = ival 执行结果一样

因为引用本身不是一个对象,所以不能定义引用的引用

引用只能绑定在对象上,而不能与字面值或某个表达式的结果绑定在一起。

指针与引用的不同点:
1.指针本身就是一个对象,允许对指针赋值和拷贝,而且在指针的声明周期内它可以先后指向几个不同的对象。
2.指针无需在定义时赋值。和其他内置类型一样,在块作用域内定义的指针如果没有被初始化,也将拥有一个不确定的值。

定义指针的方法将声明符写成*d的形式其中d是变量名。如果在一条语句中定义了几个指针变量,每个变量前都必须有符号*

int *ip1, *ip2;     //ip1和ip2都是指向int型对象的指针
double dp, *dp2;    //dp2是指向double型对象的指针,dp是double型对象

指针存放某个对象的地址,想要获取该地址,需要使用取地址符(&):

int ival = 42;
int *p = &ival;  //p存放变量ival的地址,或者说p是指向ival的指针

因为引用不是对象,没有实际地址,所以不能定义指向引用的地址。

指针的值应属于下列4种状态之一:
1.指向一个对象
2.指向紧邻对象所占空间的下一个位置
3.空指针,意味着指针没有指向任何对象
4.无效指针,也就是上述情况之外的其他值

如果指针指向了一个对象,则允许使用解引用符(*)来访问该对象:

int ival = 42;
int *p = &ival;      //p存放着变量ival的地址,或者说p是指向变量ival的指针
cout << *p;          //由符号*得到的指针p所指的对象,输出42

如果给解引用的结果赋值,实际上也就是给指针所指的对象赋值:

*p = 0;     //由符号*得到指针p所指的对象,即可经由p为变量ival赋值
cout << *p; //输出0

解引用操作仅适用于那些确实指向了某个对象的有效指针。

在这里插入图片描述

空指针不指向任何对象,在试图使用一个指针之前代码可以首先检查它是否为空。以下列出几个生成空指针的方法:

int *p1 = nullptr;    //等价于int *p = 0;
int *p2 = 0;          //直接将p2初始化为字面常量0
//首先需要#include cstdlib
int *p3 = NULL;       //等价于int *p3 = 0;

函数重载时有区别
参考https://blog.csdn.net/nice_wen/article/details/83181135

得到空指针最直接的方法就是用字面值nullptr来初始化指针,这也是C++11新标准刚引入的一种方法。 nullptr是一种特殊类型的字面值,它可以被转换成任意其他的指针类型。

指针存放的地址值相同有三种可能:
1.它们都为空
2.都指向同一个对象
3.都指向了同一个对象的下一个地址

需要注意:一个指针指向某对象,同时另一个指针指向另外对象的下一个地址,此时也可能出现这两个指针值相同的情况

void是一种特殊的指针,可用于存放任意对象的地址。可做的事有:拿它和别的指针比较、作为函数的输入或输出,或者赋给另外一个void指针。不能直接操作void*指针所指向的对象,因为我们并不知道这个对象到底是什么类型的。

double obj = 3.14, *pd = &obj;
void *pv = &obj;
pv = pd;

const限定符:
因为const对象一旦创建后其值就不能再改变,所以const对象必须初始化;
默认状态下,const对象仅在文件内有效。如果想要只在一个文件中定义const,而在其他多个文件中声明并使用它,解决办法就是对const变量不管是声明还是定义都添加extern关键字,这样只需定义一次就可以了:

//file 1.cc定义并初始化了一个常量,该常量能被其他文件访问
extern const int bufSize = fcn();
//file_1.h头文件
extern const int bufSize; //于file_1.cc中定义的bufSize是同一个

const的引用:

const int ci = 1024;
const int &r1 = ci; //正确:引用与其对应的对象都是常量
r1 = 42;            //错误:r1是对常量的引用
int &r2 = ci;       //错误:试图让一个非常量引用指向一个常量对象

允许为一个常量引用绑定非常量的对象、字面值,甚至是个一般表达式:

int i = 42;
const int &r1 = i;
const int &r2 = 42;
const int &r3 = r1 * 2;
int &r4 = r1 * 2;        //错误:r4是一个普通的非常量引用

在这里插入图片描述
指向常量的指针,不能用于改变其所指对象的值。要想存放常量对象的地址,只能使用指向常量的指针。

常量指针:把*放在const关键字之前用以说明指针是一个常量。

int errNumb = 0;
int *const curErr = &errNumb;   //curErr将一直指向errNumb
const double pi = 3.1415926;
const double *const pip = &pi;  //pip是一个指向常量对象的常量指针

指针本身是一个常量并不意味着不能通过指针修改其所指对象的值,能否这样做完全依赖于所指对象的类型:

*pip = 2.72;    //错误:pip是一个指向常量的指针

if (*curErr) {
	errorHandler();
	*curErr = 0;//把curErr所指对象的值重置

顶层const:指针本身是一个常量
底层const:指针指向的对象是一个常量

常量表达式是指值不会改变并且在编译过程就能得到计算结果的表达式。

const int max_files = 20;   //max_files是常量表达式
const int limit = max_files + 1;  //limit是常量表达式
int staff_size = 27;        //staff_size不是常量表达式
const int sz = get_size();  //sz不是常量表达式

C++11新标准规定,允许将变量声明为constexpr类型以便编译器来验证变量的值是否是一个常量表达式。声明为constexpr的变量一定是一个常量,而且必须用常量表达式初始化:

constexpr int mf = 20;   //20是常量表达式
constexpr int limit = mf + 1; //mf + 1是常量表达式
constexpr int sz = size(); //只有当size是一个constexpr函数时才是一条正确的语句

声明constexpr时用到的类型必须有所限制,将它们称为字面值类型。算术类型、引用和指针都属于字面值类型。一个constexpr指针的初始值必须是nullptr或者0,或者存储于某个固定地址中的对象。

在constexpr声明中如果定义了一个指针,限定词constexpr仅对指针有效,与指针所指的对象无关:

const int *p = nullptr; //p是一个指向整型常量的指针
constexpr int *q = nullptr //q是一个指向整数的常量指针

有两种方法可用于定义类型别名。
传统方法是使用typedef

typedef double wages;    //wages是double的同义词
typedef wages base, *p;  //base是double的同义词,p是double*的同义词 

新标准规定了一种新的方法,使用别名声明来定义类型的别名:

using SI = Sales_item;  //SI是Sales_item的同义词

C++11新标准引入了auto类型说明符,用它能让编译器替我们分析表达式所属的类型。 auto定义的变量必须有初始值

//用val1和val2相加的结果可以推断出item的类型
auto item = val1 + val2; //item初始化为val1和val2相加的结果

使用auto也能在一条语句中声明多个变量,但该语句中所有变量的初始基本数据类型都必须一样:

auto i = 0, *p = &i;  //正确:i是整数、p是整型指针
auto sz = 0, pi = 3.14;  //错误:sz和pi的类型不一致

编译器推断出来的auto类型有时候和初始值的类型并不完全一样,编译器会适当地改变结果类型使其更符合初始化规则:
1.引用被当作初始值时,以引用对象的类型作为auto的类型
2.一般忽略顶层const,同时底层const则会保留下来:

const int ci = i, &cr = ci;
auto b = ci;  //b是一个整数(ci的顶层const特性被忽略掉了)
auto c = cr;  //c是一个整数(cr是ci的别名,ci本身是一个顶层const)
auto a = &i;  //d是一个整型指针(整数的地址就是指向整数的指针)
auto e = &ci; //e是一个指向整数常量的指针(对指针对象取地址是一种底层const)

如果希望推断出的auto类型是一个顶层const,需要明确指出:

const auto f = ci;

C++11新标准引入了第二种类型说明符decltype,它的作用是选择并返回操作数的数据类型,但不实际计算表达式的值。

decltype(f()) sum = x;  //sum的类型就是函数f的返回类型

编译器并不实际调用f,而是使用当调用发生时f的返回值类型作为sum的类型。

decltype处理顶层const和引用的方式与auto有些许不同。如果decltype使用的表达式是一个变量,则decltype返回该变量的类型(包括顶层const和引用在内):

const int ci = 0, &cj = ci;
decltype(ci) x = 0;  //x的类型是const int 
decltype(cj) y = x;  //y的类型是const int&, y绑定到变量x
decltype(cj) z;      //错误:z是一个引用,必须初始化

如果decltype使用的表达式不是一个变量,则decltype返回表达式结果对应的类型。

//decltype的结果可以是引用类型
int i = 42, *p = &i, &r = i;
decltype(r + 0) b;  //正确:加法的结果是int,因此b是一个未初始化的int
decltype(*p) c;     //错误:c是int&, 必须初始化

自定义数据结构:

struct 类名 {
	类体
}

类内部定义的名字必须唯一,但可以与类外部定义的名字重复。

C++新标准规定,可以为数据成员提供一个类内初始值。创建对象时,类内初始值将用于初始化数据成员。没有初始值的成员将会被默认初始化。

头文件通常包含那些只能被定义一次的实体,如类、const和constexpr变量。

在这里插入图片描述

字符串、向量和数组

命名空间的using声明:

using namespace::name;

位于头文件中的代码一般来说不应该使用using声明。

初始化string对象的方式`:

string s1;            //默认初始化,s1是一个空串
string s2(s1);        //s2是s1的副本
string s2 = s1;       //等价于s2(s1),s2是s1的副本
string s3("value");   //s3是字面值"value"的副本,除了字面值最后的那个空字符外
string s3 = "value";  //等价于s3("value"),s3是字面值“value”的副本
string s4(n, 'c');    //把s4初始化为由连续n个字符c组成的串   

拷贝初始化,使用等号初始化;直接初始化,不使用等号。

在这里插入图片描述

如果一条表达式中已经有size()函数就不要再使用int了,这样可以避免混用int和unsigned可能带来的问题

string对象相等意味着它们的长度相同且所包含的字符也全都相同。
1.如果两个string对象的长度不同,而且较短string对象的每个字符都与较长string对象对应位置上的字符相同,就说较短string对象小于较长string对象。
2.如果两个string对象在某些对应的位置上不一致,则string对象比较的结果其实是string对象中的第一对相异字符比较的结果。

当把string对象和字符字面值混在一条语句中使用时,必须确保每个加法运算符(+)的两侧的运算对象至少有一个是string

在这里插入图片描述

范围for语句:遍历给定序列中的每个元素并对序列中的每个值执行某种操作

for (declaration : expression)
	statement

其中,expression部分是一个对象,用于表示一个序列。declaration部分负责定义一个变量,该变量将被用于访问序列中的基础元素。每次迭代,declaration部分的变量会被初始化为expression部分的下一个元素值。

//使用范围for语句和ispunct函数统计string对象中标准符号的个数
string s("Hello World!!!");
decltype(s.size()) punct_cnt = 0;
//统计s中标点符号的数量
for (auto c : s)
	if (ispunct(c))
		++punct_cnt;
	cout << punct_cnt
		 << " punctuation characters in " << s << endl; 

如果想要改变string对象中字符的值,必须把循环变量定义成引用类型。

//把字符串改写为大写字母的形式
string s("Hello World!!!");
//转换成大写形式
for (auto &c : s)
	c = toupper(c);
cout << s << endl;

在访问字符之前,首先检查s是否为空,不管什么时候只要对string对象使用了下标,都要确认在那个位置上确实有值。

//把s的第一个词改写成大写形式
//依次处理s中的字符直至我们处理完全部字符或者遇到一个空白
for (decltype(s.size()) index = 0; 
	index != size() && !isspace(s[index]); ++index)
		s[index] = toupper(s[index]);

模板本身不是类或函数,相反可以将模板看作是编译器生成类或函数编写的一份说明。

编译器根据模板创建类或函数的过程称为实例化,当使用模板时,需要指出编译器应把类或函数实例化成何种类型。

vector<int> ivec;    //ivec保存int类型的对象
vector<Sales_item> Sales_vec;   //保存Sales_item类型的对象
vector<vector<string>> file;  //该向量的元素是vector对象

在早期版本的c++标准中,必须在外层vector对象的右尖括号和其元素类型之间添加一个空格,如:vector<vector >

在这里插入图片描述

创建指定数量的元素

vector<int> ivec(10,-1);   //10个int类型的元素,每个都被初始化为-1
vector<string> svec(10,"hi!");  //10个string元素,每个都被初始化为“hi!”
vector<int> vi = 10; //错误:必须使用直接初始化的形式指定向量的大小
vector<int> v1(10);  //v1有10个元素,每个的值都是0
vector<int> v2{10};  //v2有1一个元素,该元素是10
vector<int> v3(10, 1);//v3有10个元素,每个的值都是1
vector<int> v4{10, 1};//v4有两个元素,值分别是10和1

圆括号,可以说提供的值是用来构造vector对象的。
花括号,可以描述成我们想列表初始化该vector对象。

如果初始化时使用了花括号的形式但是提供的值又不能用来列表初始化,就要考虑用这样的值来构造vector对象了。

vector<string> v5{"hi"};  //列表初始化:v5有一个元素
vector<string> v6("hi");  //错误:不能使用字符串字面值构建vector对象
vector<string> v7{10};    //v7有10个默认初始化的元素
vector<string> v8{10, "hi"}; //v8有10个值为hi的元素

向vector对象中添加元素

vector<int> v2;   //空vector对象
for (int i = 0; i != 100; ++i)
	v2.push_back(i);   //依次把整数值放到v2的尾端

在这里插入图片描述

//以10分为一个分数段统计成绩的数量:0~9,10~19,……,90~99,100
vector<unsigned> scores(11,0);   //11个分数段,全部初始化0
unsigned grade;
while (cin >> grade) {    //读取成绩
	if (grade <= 100)	  //只处理有效成绩
		++scores[grade/10]; //将对应分数段的计数值加1
}

vector对象(以及string对象)的下标运算符可用于访问已存在的元素,不能用于添加元素

在这里插入图片描述
有效的迭代器或者指向某个元素,或者指向容器尾元素的下一位置,其他情况都属于无效。

//b表示v的第一个元素,e表示v尾元素的下一个位置
auto b = v.begin(), e = v.end(); //b和e的类型相同

end成员返回的迭代器常被称作尾后迭代器,表示我们已处理完了容器中的所有元素。

在这里插入图片描述
执行解引用的迭代器必须合法并确实指示着某个元素

string s("some string");
if (s.begin() != s.end() ) {	//确保s非空
	auto it = s.begin();		//it表示s的第一个字符
	*it = toupper(*it);         //将当前字符改写成大写形式
}

因为end返回类型的迭代器并不实际指示某个元素,所以不能对其进行递增或解引用操作。

//依次处理s的字符直至我们处理完全部字符或者遇到空白
for (auto it = s.begin(); it != s.end() && isspace(*it); ++it)
	*it = toupper(*it);  //把当前字母改成大写形式

只要我们养成使用迭代器和!=的习惯,就不用太在意用的到底是哪种容器类型。

begin和end返回的具体类型由对象是否是常量决定,如果对象是常量,begin和end返回const_iterator;如果对象不是常量则返回iterator。

为了便于专门得到const_iterator类型的返回值,C++11新标准引入了两个新函数,分别是cbegin和cend:

vector<int> v;
auto it3 = v.cbegin();  //it3的类型是vector<int>::const_iterator
(*it).empty();   //解引用it,然后调用结果对象的empty成员
*it.empty();     //错误:试图访问it的名为empty的成员,但it是个迭代器

箭头运算符->把解引用和成员访问结合在一起。

//依次输出text的每一行直至遇到第一个空白行为止
for (auto it = text.cbegin();
	it != text.cend() && !it->empty(); ++it)
	cout << *it << endl;

任何一种可能改变vector对象容量的操作,都会使迭代器失效。

在这里插入图片描述
定义数组的时候必须指定数组的类型,不允许用auto关键字又初始值的列表推断类型。数组存放的是对象。

不能将数组的内容拷贝给其他数组作为初始值,也不能用数组为其他数组赋值。

int *ptrs[10];    //ptrs是含有10个整型指针的数组
int &refs[10] = /*?*/;    // 错误:不存在引用的数组
int (*Parray)[10] = &arr;    //Parray指向一个含有10个整数的数组
int (&arrRef)[10] = arr;     //arrRef引用一个含有10个整数的数组
int ia[] = {0,1,2,3,4,5,6,7,8,9};
auto ia2(ia);  //ia2是一个整型指针,指向ia的第一个元素
ia2 = 42;      //错误:ia2是一个指针
decltype(ia) ia3 = {0,1,2,3,4,5,6,7,8,9};
ia3 = p;    //错误:不能用整型指针给数组赋值
ia3[4] = i; //正确:把i的值赋给ia3的一个元素

C++11新标准引入了两个名为begin和end的函数,这两个不是成员函数。

int ia[] = {1,2,3,4,5,6,7,8,9};
int *beg = begin(ia);   //指向ia首元素的指针
int *last = end(ia);    //指向arr尾元素的下一个位置的指针

在这里插入图片描述
在这里插入图片描述
数组见第三章。。。看不下去了。回头再看。

严格来说,C++语言中没有多维数组,通常所说的多维数组其实是数组的数组。

多维数组的初始化:

int ia[3][4] = {
	{0,1,2,3},  //第一行的初始值
	{4,5,6,7},  //第二行的初始值
	{8,9,10,11} //第三行的初始值
};

等价于:

int ia[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
//显式地初始化每行的首元素
int ia[3][4] = {{0}, {4} ,{8}};
//显式地初始化第一行,其他元素执行值初始化
int ix[3][4] = {0,3,6,9};
//用arr的首元素为ia的最后一行的最后一个元素赋值
int (&row)[4] = ia[1];   //把row绑定到ia的第二个4元素数组上

使用范围for语句处理多维数组,除了最内层的循环外,其他所有循环的控制变量都应该是引用类型。

int ia[3][4];  
int (*p)[4] = ia;  //p指向含有四个整数的数组
p = &ia[2];        //p指向ia的尾元素

表达式

C++11新标准规定商一律向0取整,除了-m导致溢出的特殊情况,其他时候(-m)/n和m/(-n)都等于-(m/n),m%(-n)=m%n,(-m)%n等于-(m%n)。

string s1 = "a string", *p = &s1;
auto n = s1.size();   //运行string对象s1的size对象
n = (*p).size();      //运行p所指对象的size成员
n = p->size();        //等价于(*p).size()

解引用运算符的优先级低于点运算符。

条件运算符:

cond?expr1:expr2;

嵌套条件运算符:

finalgrade = (grade >90) ? "High pass" : (grade < 60) ? "fail" : "pass";

条件运算符满足右结合律。
条件运算符的优先级非常低,因此当一条长表达式中嵌套了条件运算子表达式时,通常需要在它两端加上括号。

位运算符建议用于处理无符号类型。

sizeof满足右结合律,,且并不实际求运算对象的值。

在这里插入图片描述
形参列表中的形参通常用逗号隔开,其中每个形参都是一个含有一个声明符的声明。即使是两个形参类型一样,也必须把两个类型都写出来。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值