C++程序设计语言Note

第一部分 引言

二、C++概览:基础知识

1.  引言
2.  基本概念(hello world,变量、类型和算术运算(auto关键字),常量(const、constexpr编译期求值),检验和循环,指针、数组和循环)
3.  用户自定义类型(结构,类,枚举)
3.  模块化(分离编译,命名空间,错误处理(异常、不变式、静态断言))

三、C++概览:抽象机制

2.  (具体类型(算术类型complex、容器、初始化容器),抽象类型,虚函数,类层次(unique_ptr的应用);)
3.  拷贝和移动(拷贝容器(拷贝构造函数、拷贝赋值运算符),移动容器(利用右值引用&&和std::move将左值转换为右值引用从而实现移动操作),资源管理(用move管理资源、unique_ptr资源管理句柄),抑制操作(对于层次中的某些类,防止实例化时采用=delete的抑制操作);)
4.  模板(参数化类型typename T,函数模板,函数对象(function object的优点是可以带参被传入算法,lambda表达式及其捕获方式),可变参数模板,别名(typename和using);)

四、C++概览:容器与算法

1.  标准库(标准库概述(标准库设施类型),标准库头文件与命名空间,字符串(连接操作、子串、比较实例);)
2.  字符串(连接操作、子串、比较实例)
3.  I/O流(输出、输入、用户自定义类型I/O(istream& operator>>()与ostream& operator<<()用于以特定格式输入存储;)
4.  容器(vector(元素、范围检查与at),list,map,unordered_map,容器概述(容器、容器适配器、特殊类容器类型、容器基本操作);)
5.  算法(使用迭代器,迭代器类型,流迭代器(一般用作实参传入算法),谓词Predicate,算法概述,容器算法)

五、C++概览:并发与实用功能

2.  资源管理(资源管理句柄,标准库的锁mutex及unique_ptr实例,unique_ptr与shared_ptr(管理资源或者用作多态基类指针或多个指针共享对象时);)
3.  并发(任务和thread(thread类型传入函数或函数对象进行实例化,join函数),传递参数(用函数实例化thread时用的是可变模板参数特性,用函数对象时用的是函数对象的可带参传入特性),返回结果(通过传递参数的方式返回任务函数的结果),共享数据(互斥对象mutex和lock函数获取互斥对象、等待事件:生产者消费之模型与利用了condition_variable的线程间通信),任务通信(future和promise在任务间传值、packaged_task类型简化任务连接future和promise的设置、async启动可异步运行的任务);)
4.  小工具组件(时间(chrono头文件及相关时间函数),类型函数(元编程、iterator_traits与decltype、标签分发与iterator_type、iterator_category、类型谓词如is_arithmetic),pair和tuple)
5.  正则表达式(regex定义、regex_search)
6.  数学计算(数学函数和算法,复数(complex模板),随机数(随机数生成器、随机数生成引擎、数学分布,Rand_int类),向量算术(valarray模板),数值限制(limits、numberic_limits);)
第二部分 基本功能

六、类型与声明

1.  ISO C++标准(实现(宿主式和独立式),基本源程序字符集)
2.  类型(基本类型,布尔值(指针也可以转换为bool类型),字符类型(char、signed char、unsigned char、字符字面值常量、wchar_t、char16_t、char32_t、Unicode字符集),整数类型(int、unsigned、signed、short、long、long long、csdtint头文件、整数字面值常量进制与符号说明、整数字面值常量的类型),浮点数类型(float、double和long double),前缀和后缀,void(表函数伪返回和表指针所指对象类型未知),类型尺寸,对齐(alignof运算符返回实参的对齐情况、alignas(T)像T那样对齐);)
3.  声明(声明的结构(5个部分),声明多个名字(注意指针等的运算符只作用于第一个名字),名字(关键字),作用域,初始化(最好除了auto声明的变量其它都用列表初始化{}的方式、缺少初始化器时一般会执行默认初始化但堆变量不会、初始化器列表),推断类型auto和decltype(auto类型修饰符、当用auto时不要用{}初始化、decltype:当不想在类型推断时定义一个初始化变量时用decltype);)
4.  对象和值(左值和右值(左值、特别值和右值以及区分的两个属性有身份和可移动),对象生命周期(自动对象、静态对象、自由存储对象、临时对象、线程局部对象);)
5.  类型别名(using和typedef,using可以引入一个模板别名,不允许在类型别名前加修饰符)

七、指针、数组与引用

2.  指针(void*(除了函数指针和类指针,void*可以指向其它任何类型,但不能进行解引用或++之类操作,需要用static_cast<type*>显式转换回某一type),nullptr(用于表示不指向任何对象的指针);)
3.  数组(数组的初始化器(数组不能赋值,只能初始化),字符串字面值常量(字符串字面值常量与C风格字符串一样都以\0结尾、原始字符串:当遇到大量反斜线的字符串时,可以用R"(ccc)“或R”(***ccc***)"的写法表示原始字符串),大字符集(前缀是L的字符串由宽字符组成,前缀u与UTF编码对应关系、uR顺序关系);)
4.  数组中的指针(数组漫游,多维数组,传递数组(用指针或包装了vector的指针传递);)
5.  指针与const(const*和*const以及const type*区别,其中const type*与type const*表义一样)
6.  指针与所有权(一种好的管理资源的做法是将表示某种所有权的指针全都置于vector、string和unique_ptr等资源句柄类中)
7.  引用(左值引用,const左值引用,右值引用(move函数与static_cast<X&&>),引用的引用(引用合并),指针与引用(指针与引用的选择,如让一个名字永远对应一个对象时用引用、重载运算符时最好用引用、想让集合中的元素指向对象时用指针);)

八、结构、联合和枚举

2.  结构(struct)(struct的布局(在struct的对象中,成员按照声明的顺序依次存放,注意对齐方式),struct的名字(struct的名字一出现就可以使用了,即可以出现strcut a{a* a1};,但是未定义时不能成员的名字和结构的大小),结构与类(struct是一种class,它的成员默认是public的),结构与数组(可以用结构包含数组,也可以数组元素为struct,标准库中array即是一个模板struct),类型等价(对于两个struct来说,即使它们成员相同,它们本身仍是不同的类型;struct类型与其成员类型不能混为一谈),普通旧数据(POD是指能被“仅当作数据”处理的对象,程序员无需顾及类布局的复杂性以及用户自定义的构造、拷贝和移动语义;这么做的主要原因是在硬件条件允许的范围内尽可能高效地移动对象,比如调用100次拷贝构造函数比str::memcpy更费时,一般在vector等的底层应用;is_pod判断),域(又称为位域,通过域可以将几个变量打包在一个字节内,但注意这并不一定能节省空间);)
3.  联合(一种特殊的struct,所有成员分配在同一地址空间上,联合与类,匿名Union(标签联合、匿名联合);)
4.  枚举(enum class(限定了作用域的强类型枚举,枚举基础类型,枚举和switch的结合应用,枚举类类型不支持隐式类型转换,必须显式转换它),普通的enum(支持枚举元素隐式类型转换为int,普通enum不一定要加作用域限定,所以可能出现命名空间污染),未命名的enum(可以用来声明一组常量);)

九、语句

2.  语句概述(C++语句的形式化定义)
3.  声明作为语句(一个声明就是一条语句,除非被声明为static,否则在控制线程传递给当前声明语句的同时执行初始化器,绝大多数情况下,如果没有为变量找到一个合适的值,暂时不要声明它)
4.  选择语句( if(condition)语句;if(condition)语句else语句;switch(condition)语句;condition可能是一个语句,也可能是一个声明;@1 if语句:if语句condition的短路特性,enum class不能隐式转换为bool类型,其它类型可以;@2 switch语句:注意每一条语句是否要有break,default用作最常用case和取值错误的分支,在switch case到枚举对象时最好不要用default,case语句中声明必初始化;switch语句和enum或enum class联合使用; @3 条件中的声明:尽量延迟局部变量的定义,直到能给它赋初值为止)
5.  循环语句( while(条件)语句;do语句while(表达式);for(for初始化语句 条件可选;表达式可选)语句;for(for声明:表达式)语句;其中for初始化语句要么是一个声明,要么是一条表达式语句,它们都以分号结束;@1 范围for语句:操作对象是整个序列,当需要改变序列内的值或者所传对象很大时,可以采用传引用的方式;@2 for语句:auto关键字可用来进行初始化时类型推断;@3 while语句;@4 do语句;@5 退出循环:break、continue;)
6.  goto语句(跳出外层循环,但尽量不要用goto;)
7.  注释与缩进(好的注释是较高抽象层次的注释)

十、表达式

2.  一个桌面计算器实例(@1 分析器:递归下降的语法分析,expr()、term()、prim()定义;@2 输入:首先说明Token_stream的完整定义,然后对输入的数据进行类型判断,是number还是运算符还是名字,判断完再用putback放回流中以便用判断后的类型来读取;enum kind可以用value.kind对应到enum中;@3 底层输入:为了解决要求用户必须在末尾加分号和要求用户以空格结尾分隔出名字的问题,利用get()读取单字符的方式来解决问题,检测函数isspace()、isdigit()、isalpha()、isalnum()等;@4 错误处理:统计错误数量和返回一个值确保程序可继续执行策略;@5 驱动程序:calculate函数和main函数控制执行以及错误输出策略;@6 头文件;@7 命令行参数:argc,crgv[ ],可以用vector先存储argv;@8 风格:应用了STL内容;)
3.  运算符概述(运算符一览,@1 结果:算术运算符结果类型由一组称为“常见算术类型转换”的规则来确定的;关系运算符结果是布尔值;对于接受左值运算对象的运算符,它的结果是一个左值;sizeof和指针相减的结果是size_t和ptrdiff_t,溢出和除以0不会报标准异常;@2 求值顺序:注意一些未定义的顺序如v[i]=i++,注意关系运算符短路求值;@3 运算符优先级:注意&与==是==优先的;@4 临时对象:编译器在计算表达式结果时,用临时变量存储中间值,注意使用时防止临时变量销毁后还在使用,但可以将临时变量用作const引用或命名对象的初始化器;)
4.  常量表达式(constexpr编译时求值;@1 符号化常量:将字面值替换为符号化常量,增强阅读性;@2 常量表达式中的const:以常量表达式初始化的const可以用在常量表达式中,与constexpr不同的是,const可以用非常量表达式初始化,但是该const将不能用作常量表达式;@3 字面值常量类型:含有constexpr构造函数的类称为字面值常量类型,constexpr构造函数必须函数体为空且所有成员都是用潜在的常量表达式初始化的,且constexpr隐含了const的意思;@4 引用参数:const引用引用的是值,因此引用参数也可以作为constexpr函数的参数,如constexpr complex<double> z3(z1),其中z1是constexpr complex<float>型;@5 地址常量表达式:全局变量等静态分配的对象地地址是一个常量,然而该地址由链接器分配,所以不能用constexpr求取其地址有关的值;)
5.  隐式类型转换(@1 提升:保护值不被改变的隐式类型转换称为提升,常常提升到“自然”尺寸而不是long或long double;@2 类型转换:{}可以防止窄化类型转换,当要使用窄化类型转换时最好使用narrow_cast<>();&2.1整数类型转换:目标类型时unsigned时,转换前的整数值对2的n次幂取模后所得的结果值就是转换结果,其中n是目标类型所占的位数;若目标类型是signed的,则当原值能用目标类型表示时,它不发生改变,反之,结果依赖于具体实现;&2.2浮点数类型转换:若原值能用目标类型完整地表示,则所得结果与原值相等;若原值介于两个相邻的目标值之间,则结果取它们中的一个;&2.3指针和引用类型转换:任何指向对象的指针都能隐式地转换为void*,指向派生类指针可以隐式转换为基类指针;求值结果为0的常量表达式可隐式转换为任意指针类型的空指针;&2.4指向成员的指针的类型转换;&2.5布尔值类型转换:指针、整数和浮点数都可以隐式地转换为bool类型;&浮点数向整数类型转换;@3 常用的算术类型转换;)

十一、选择适当的操作

1.  其它运算符(@1 逻辑运算符:逻辑与&&、逻辑或||和逻辑非!;@2 位逻辑运算符:与&、|或、^异或、~非、>>右移和<<左移;位逻辑运算符常用于实现一个小集合的概念(位向量),可以结合enum使用检查流状态或从字中抽取位域;@3 条件表达式:c?e1:e2;@4 递增与递减:++和–操作符)
2.  自由存储(new会在堆上分配空间,初始化new的对象时最好用{ };@1 内存管理:内存泄漏、提前释放和重复释放三个问题,避免方法:避免“裸”new和RAII技术,RAII即把它的指针放在一个管理器对象(有时也称句柄)如vector、unique_ptr等中;@2 数组:new数组与delete数组时的注意[ ];尽量不要new一个局部对象,防止在未delete时函数发生return;@3 获取内存空间:当new发生错误时返回bad_alloc异常;@4 重载new:放置语法new(size_t sz,void* p)可以在特定位置申请内存;@5 nothrow new :用法new(nothrow) type,若出错不会返回bad_alloc,而是返回nullptr)
3.  列表(可用{ }初始化对象,也可以有其它用法;@1 实现模型:被用作构造函数实参,被用于初始化一个聚合体,被用于构建一个initializer_list对象,{ } 只可以被用来拷贝,不能用来移动;@2 限定列表:S v{1,2};等价于v=S{1,2};等价于S *p=new S{1,2};@3 未限定列表:标准库类型initializer_list<T>可以用于处理长度可变的列表,如int func(initializer_list<int> val)可以直接传{1,2}或者{1,2,3},即可以传同质、变长列表;且只有当列表内类型相同时,才可以auto推断列表类型;但是不能通过推断未限定列表的类型使其作为模板的实参;)
4.  lambda表达式(可以用lambda表达式实现回调,lambda表达式组成部分有:一个捕获列表、一个可选参数列表、一个可选mutable修饰符、一个可选noexcept修饰符、一个可选->尾置返回列表和一个表达式体;@1 实现模型:可把lambda表达式看成是一种定义并使用函数对象的便捷方式,把由lambda生成的类的对象称为闭包对象,简称闭包;@2 lambda的替代品:可以用函数对象,可以为lambda表达式命名,形式为auto funcName=lambda表达式语句;@3 捕获:空捕获[ ],引用捕获[&],值捕获[=];& 3.1 lambda表达式与生命周期:lambda表达式生命周期有可能比它的调用者更长,这时要确保其捕获用值捕获的方式将副本都拷贝到闭包对象中,否则会产生错误;& 3.2命名空间名字:由于全局变量永远是可访问的,所以不用捕获它们,但要确定它们在作用域内;& 3.3lambda与this:当lambda被用在成员函数中,我们可以将this添加到捕获列表中,这样就可以访问类的成员了,注意this是以引用方式捕获的;& mutable的lambda:一般情况下lambda默认是一个const成员函数,但你可以显式声明为mutable;@4 调用与返回:当lambda有一个以上的return时,需要通过->显式声明返回类型;@5 lambda的类型:lambda表达式是一种函数对象的类型,是局部类类型,是闭包类型;lambda表达式可以被用来初始化一个声明为auto或std::function<R(AL)>的变量,且如果一个lambda表达式什么也不捕获,则可以将它赋给一个指向正确函数类型的函数指针;)
5.  显式类型转换(显式类型转换三类,一是基于{ }的构造;二是const_cast、static_cast、reinterpret_cast和dynamic_cast;三是C风格的类型转换;也可以用自定义的narrow_cast;@1 构造:通过{ }构造所产生的类型转换是行为良好的,是非窄化转换;@2 命名转换:是一种强制类型转换,责任自负;static_cast执行关联类型转换,reinterpret_cast执行非关联类型转换,const_cast参与类型只在const和volatile修饰符上有变化,dynamic_cast执行由指针或引用向类层次体系的转换,并执行运行时检查;@3 C风格的转换:可能根据源类型和目标类型调用上面命名转换所述的一种转换方式;@4 函数形式的转换:type(val)被称为函数形式的转换,大部分这种行为与C风格的(Type)val转换一样;)

十二、函数

1.  函数声明(注意类的成员函数加上作用域声明;@1 为什么使用函数:结构化机制,分解成有意义的小的块;@2 函数声明的组成要件:函数名、参数列表、返回类型、inline、constexpr、noexcept、链接说明如static、[[noreturn]];成员函数还可以被限定为:virtual、override、final、static和const;@3 函数定义:声明与定义语句形参名不一定一样,但最好一致;可以在形参列表中有未命名参数表示传入但在函数体内未使用该参数;@4 返回值:除了构造和析构函数不需要返回类型,其它都需要返回类型;可以使用auto结合->尾置返回类型的方式返回值;void函数可以无返回值;返回局部变量的指针或引用是不安全的;return的5种形式;@5 inline函数:告诉编译器为函数生成内联代码;@6 constexpr函数:可以将简单的函数声明为constexpr确保其在编译器求值;& 6.1constexpr与引用:constexpr函数不允许有副作用,因此虽然constexpr函数接受引用实参,但不可以更改它(非局部变量);constexpr函数可以接受一个const限定的变量;& 6.2 条件求值:constexpr函数之外的条件表达式不会在编译时求值,这意味着它可以请求运行时求值;@7 [[noreturn]]函数:形如[[…]]的机制被称为属性,置于函数开头的[[noreturn]]表明我们不希望函数返回任何结果;@8 局部变量:局部变量每次调用都会初始化,但static变量只初始化一次,且每次使用的都是一个变量;)
2.  参数传递(@1 引用参数:传递的变量很小时,用值传递,要更改实参时,用引用传递或指针传递,为了效率而不想改变实参时,用const引用传递等;@2 数组参数:当数组作为函数参数时,实际传入的是指向该数组首元素的指针,但传指针的方法不能确定数组长度,因此可以再多一个数组长度的参数或者使用数组引用,但数组引用的方法灵活性比较差;@3 列表参数:一个由{ }限定的列表可以作为以下形参的实参:类型std::initializer_list<T>、能用列表中的值初始化的类型T和T类型数组的引用,其中类型std::initializer_list<T>在具有二义性时被优先考虑为被转化的类型;@4 数量未定的参数:可以用可变模板、initializer_list<T>或用…省略号结束参数列表,第三种最好只在与C语言程序的接口使用;@5 默认参数:最好避免使用值会改变的默认参数,有默认值的参数不能在没有默认值得参数前面,在一个作用域得一些列声明中,默认值不能重复或改变;)
3.  重载函数(@1 自动重载解析:重载匹配顺序:精确匹配、执行提升后匹配、执行标准类型转换后实现匹配、执行用户自定义的类型转换后实现匹配和使用函数声明种的省略号…进行匹配,若某次函数调用在能找到匹配的最高层级上发现了不止一个匹配,则本次调用会因为产生二义性而被拒绝;@2 重载与返回类型:重载解析时不考虑返回类型;@3 重载与作用域:重载只会发生在与调用函数相同的作用域内,基类和派生类作用域不同,默认情况下不会重载,但可以使用using声明使其可能重载;@4 多实参解析:与单实参类似,但若匹配时向两个都是同一层级的(且是最高层级),则会因二义性而拒绝调用;@5 手动重载解析:可以使用static_cast<T>将引发二义性的参数转换为匹配的版本,但更好的做法是再写一个匹配的重载函数;)
4.  前置与后置条件(把函数调用时应该遵循的约定称为前置条件,把函数返回时应该遵循的约定称为后置条件,应该有一些举措确保前置条件和后置条件被满足;)
5.  函数指针(程序员只能对函数做两种操作,调用或者取得地址,解引用函数指针可以用*,也可以不用,同样,获取函数地址时,既可以用&,也可以不用;使用指针赋值操作时,要求完整的函数类型必须精确匹配;函数指针可以被用来做算法的参数;)
6.  (最好不要使用宏;@1 条件编译;@2 预定义宏:常见预定义宏,多为__开头,__结尾;@3 编译指令:有别于C++标准或之外的,一般用#pragma行定义;)

十三、异常处理

1.  错误处理(包括异常安全保障和RAII技术;@1 异常:主调组件若象处理某些失败的情形,可以把这些异常置于try块的catch语句中;被调语句无法完成既定的任务时,可以用throw语句抛出一个异常说明情况;异常是被程序抛出的一个对象,最简单的定义异常的方法就是为一种错误定义一个专门的类;@2 传统的错误处理:终止程序,返回错误值,返回合法值但程序处于“错误状态”,调用错误处理函数;@3 渐进决策:对于未捕获的异常的处理模式是终止程序,这是渐进决策的思想;@4 另一种视角看异常:& 4.1异步事件:C++异常用于处理同步事件,异步事件不属于异常范畴;& 4.2不是错误的异常:程序员应当坚持异常都是错误的观点,不应该将try语句块应用到其它正常的控制语句中;@5 何时不应应用异常:嵌入式的某个关键性组件和规模巨大的旧系统,可以用模仿RAII在每个含有构造函数的类中增加一个invalid()操作以返回一些error_code或者构建一个既能返回结果又能返回异常的函数,返回的是pair<Value,error_code>;@6 层次化错误处理:错误处理应当尽量层次化,按照自底向上的顺序,每一层级在它力所能及的范围内处理尽量多的错误,把剩下的错误留给更高层级处理,且若函数检测到一个运行时错误,就不应该向它的调用者需求帮助或者请求资源了,这类请求会让系统的依赖关系形成环状结构;@7 异常与效率:尽量把异常处理留给异常处理机制,noexcept说明符用于让函数不会抛出异常,当产生异常时直接结束程序;)
2.  异常保障(当通过抛出异常终止某个操作后,程序应仍然处于有效状态,则称这个操作是异常安全的操作;标准库的组件对它的操作有基本保障、强保障和不抛出保障;函数在抛出异常前必须先释放文件、锁、网络连接和线程等资源;)
3.  资源管理(使用“资源获取即初始化”技术,用含有构造和析构函数的类的对象来处理请求和释放资源的问题,对象在其作用域末尾被销毁,析构函数负责释放资源,构造函数负责获取资源时才初始化,并且在无法初始化时抛出异常,这种技术也称“句柄类”;@1 finally:为编写任意代码在异常发生后的清理工作,人们发明了各种“最终的”语言概念,比如定义finally通过构造函数的参数提供“最终操作”;)
4.  强制不变式(两种做法对待前置条件不满足:不断调试不让此类情况发生和终止程序;断言,运行时断言和编译时断言;)
5.  抛出与捕获异常(@1 抛出异常:可以throw任意类型的异常,前提是它可以被复制或移动,捕获的异常对象其实是抛出异常的一份拷贝;& 1.1noexcept函数:noexcept不会抛出异常,遇到异常时直接结束程序;&1.2 noexcept运算符:通过noexcept(常量表达式)来声明有条件的noexcept,其中常量表达式可以是谓词(Predicate),也可以是另一个noexcept表达式;& 1.3异常说明:老式的C++中采用throw()或throw(True,False)来进行异常说明;@2 捕获异常:满足异常处理程序的条件,try和catch的{ }都是一个局部作用域;& 2.1重新抛出:当内部异常处理程序不能完整地处理该错误,此时,异常处理程序先完成能在局部完成的任务,然后再抛出异常,通过这种方式,异常可以很好被处理;& 2.2捕获每个异常:标准库提供std:;exception包含所有标准库异常;可以用catch(…)表示捕获任意异常;& 2.3多异常处理程序:一个try可以对应多个catch语句,异常处理将会依次(书写顺序)尝试每段处理代码;& 2.4函数try块:可以把整个函数体写在try块里,对于构造函数,catch语句最应该做的是抛出异常,可以在catch从句的结尾写一个throw;& 2.5终止:处理异常的时候不要抛出新异常,不要抛出不能处理的异常;系统在遇到几种情况时会终止,而程序员也可以选择自己调用terminate或者abort等终止程序;@3 异常与线程:若异常在线程中未被捕获,则会终止程序;可以用标准库current_exception函数把某一线程的异常传递给另一线程的处理程序;)
6.  vector的实现(@1 一个简单的vector:vector构造时两处可能发生异常,一处allocate时内存不足,一处是对象无法复制,可以用自己写的catch销毁语句,也可以用initialized_fill帮助实现构造函数;@2 显式地表示内存:利用vector_base专门处理vector的内存,确保其被销毁时内存被回收;利用initialized_fill、initialized_copy、和clear等帮助构造、拷贝赋值和移动;@3 赋值:两种实现,一种是先分配,拷贝再交换,隐式销毁旧值,一种直接构造临时变量,再swap以及它的优化版本;@4 改变尺寸:& 4.1reserve:两种方法,一种创建新vector,然后拷贝、存入新值;& 4.2resize;& 4.3push_back;& 4.4最后一点思考;)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值