每周100个C++知识点(备忘录)(六)

001-011 包含对象成员的类

1.C++的一个主要目标是促进代码重用,公有继承是一种方法,另外的方法是:①使用本身是另一个类对象的类成员,这种方法称为包含、组合或层次化;②使用私有或保护继承,都用于实现has-a关系,即新的类将包含另一个类的对象;③类模板

2.在类中可以使用已经定义好的类对象,可以使用string类、valarray类等等,用于数值处理的是valarray类,由头文件valarray支持,支持将数组中所有元素值相加、数组中查找最大最小值等操作。valarray被定义为一个模板类

3.模板类意味着声明对象时,必须指定具体的数据类型,语法是:valarray name;,代表一个type类型的数组

4.初始化valarray数组的方法,是调用它的构造函数,语法是valarray v1;(一个size=0的double数组);valarray v2(8);(一个size=8的int数组);valarray v3(10,8);(一个size=8的初值为10的int数组);valarray v4(arr1,4);(把常规double数组arr1的前4项作为v4的值);也可以使用初始化列表:valarray v5={val1,val2,…}

5.这个类的一些方法:①访问各个成员:operator;②返回包含的元素数:size();③返回所有元素的和:sum();④返回最大的元素:max();⑤返回最小的元素:min()

6.“包含”是has-a关系,也就是说,学生有姓名、考试成绩,但string类不是student类的基类,而student类包含了string类的对象

7.对于数据成员,如果它本身是一个类对象,则应被声明为私有的,则成员函数可以通过类对象本身的公有接口来访问和修改数据成员,这被描述为:”类获得了其成员对象的实现,但没有继承接口“

8.使用公有继承时,类可以继承接口,可能还有实现(已经定义好的内联函数),获得接口是is-a关系的组成部分,而使用组合,类可以获得实现,但不能获得接口,这是has-a关系的一部分

9.复习:explicit,可以用一个参数调用的构造函数将用作从参数类型到类类型的隐式转换函数,explicit关闭隐式转换,因为隐式转换有可能带来不知所云的结果。使用explicit后,编译器认为隐式类型转换是错误的

10.复习:对于继承的对象,构造函数在成员初始化列表中使用类名来调用特定的基类构造函数,对于成员对象,构造函数则使用成员名。初始化列表中的每一项都调用与之匹配的构造函数;如果不适用初始化列表语法,C++将使用成员对象所属类的默认构造函数

11.初始化顺序:当初始化列表中包含多个项目时,这些项目被初始化的顺序为它们被声明的顺序,而不是它们在初始化列表中的顺序;当一个成员的值作为另一个成员的初始化表达式的一部分时,必须注意初始化顺序

012-028 私有继承

12.C++提供的另一种实现has-a关系的途径是私有继承,使用私有继承,基类的公有和保护成员都将称为派生类的私有成员,意味着基类方法将不会成为派生类对象公有接口的一部分,但可以在派生类的成员函数中使用它们

13.使用私有继承,类将继承实现,派生类可以调用派生类的方法使用基类方法来访问基类组件。包含将对象作为一个命名的成员对象添加到类中,而私有继承将对象作为一个违背明明的继承对象添加到类中,使用术语子对象表示通过继承或包含添加的对象

14.私有继承语法,class derived_class : private base_class1, private base_class2,…;使用多个基类的继承被称为多重继承(MI),通常公有MI将导致一些问题,需要额外的语法规则来解决它们

15.使用私有继承,在用对象时,对象是未命名的,而使用包含时,对象是命名的。对于私有继承,不需要继承类对象的私有数据

16.隐式地继承组件而不是成员对象将影响代码编写,必须使用用于公有继承的技术,对于包含,将使用对象名+初值来初始化列表;对于私有继承类,将使用对象的构造函数初始化列表,使用类名而不是成员名来标识构造函数

17.访问基类的方法时,包含将使用.运算符来通过数据成员名称来访问方法;对于私有继承,需要使用域解析运算符来调用方法

18.访问基类对象时,包含将返回(或使用)对象的名称来在函数中实现相关功能,但私有继承没有名称,所以使用* this+强制类型转换的方法,将* this转化为对象类型

19.访问基类的友元函数:用类名显式地限定函数名不适合于友元函数,因为友元函数不属于类,但可以通过显式地转换为基类来调用正确的函数

20.对于多重继承的友元函数,不论私有继承还是公有继承,如果多个基类中都定义类类似的友元函数,都必须显式地强制类型转化为指定基类,因为如果直接使用,将会导致编译器不知道使用哪个基类的友元函数

21.使用包含还是使用私有继承?大多数人觉得该使用包含。包含可以使用更多的类,减少复杂度,同时可以包含同一个类的多个对象,而私有继承将只能包含一个对象

22.如果类包含保护成员,则这样的成员在派生类中是可用的,但在继承层次结构外是不可用的。如果使用组合将这样的类包含在另一个类中,则后者不是派生类,而位于继承层次结构之外,不能访问保护成员,但继承得到的将是派生类

23.需要使用私有继承的情况:需要重新定义虚函数,派生类可用重新定义虚函数,但包含类不可以,使用私有继承,重新定义的函数将只能在类中使用,不是公有的

24.通常,使用包含来has-a关系,如果新类需要访问原有类的保护成员,或需要重新定义虚函数,则应该使用私有继承

25.保护继承,是私有继承的变体,在列出基类时使用关键字protected,使用保护继承,基类的公有成员和保护成员都将称为派生类的保护成员,基类的接口在派生类中是可用的,但在继承层次结构之外是不可用的

26.使用私有继承时,第三代将不能使用基类的接口,因为基类的公有方法在派生类中将编程私有方法;使用保护继承时,基类的公有方法在第二代中将变成受保护的,因此第三代可用使用它们

27.使用保护派生或私有派生时,基类的公有成员将称为保护成员或私有成员。假设要让基类的方法在派生类外可用,有两个方法:①定义一个使用该基类方法的派生类方法,可用通过域解析运算符使用;②将函数调用包装在另一个函数调用中,使用using来指出派生类可用使用的特定基类成员,即使采用私有派生也可以

28.在使用using声明时,只用通过域解析运算符说明可以在派生类中使用的方法的名称,而不加圆括号、函数特征标和返回类型

029-044 多重继承

29.MI描述多个直接基类的类,也表示is-a关系,必须使用关键字public限定每一个基类,因为除非特别指出,编译器将默认是私有派生,而私有和保护都是表示has-a关系

30.MI带来的问题:①从两个不同的基类继承同名方法;②从两个或更多相关基类那里继承同一个类的多个实例

31.MI继承的基类,如果有一个公共基类,其多个派生类被用作基类进行MI(如定义一个工人类,其派生类为服务员类、歌手类,则会唱歌的服务员),则该派生类中包含了多重公共基类定义,通常将派生类对象的地址赋给基类指针,则现在出现了二义性,因为通常赋值将把基类指针设置为派生对象中的基类对象的地址,而此时有多个地址可选。解决方案是,指定对象时使用类型转换

32.本质上的问题是,根本不需要那很多个公共基类,只需要一个就可以了。解决这个问题的C++方法是定义一个虚基类,它使从多个类(基类相同)派生出的对象只继承一个基类对象。语法是,在声明基类时增加virtual,该限定符与public和private等的顺序无关紧要

33.疑问①:为什么使用术语“虚”?虚函数和虚基类之间并不存在明显联系;C++反对引入新的关键字;virtual类似于关键字重载

34.疑问②:为什么不抛弃将基类声明为虚的这种方式,而使虚行为称为多MI的准则?因为①一些情况下可能需要多个基类的拷贝;②将基类作为虚的要求程序完成额外计算,不应当为其付出代价;③这样做是有缺点的

35.疑问③:这样做是否存在麻烦?是的,为了使虚基类能够工作,必须调整一些规则,以不同的方式编写代码。使用虚基类还可能需要修改已有代码

36.使用虚基类时,需要对类构造函数采用一种新的方法:对于非基类,唯一可以出现在初始化列表中的构造函数是即时基类构造函数函数,但这些构造函数可能需要将信息传递给基类,但在信息自动传递时,有可能出现多种路径,因此不能将基类参数直接传递给初始化列表

37.C++在基类是虚的时,禁止信息通过中间类自动传递给基类,而会调用基类的默认构造函数,如果不希望使用基类的默认构造函数,则需要显式地调用所需的基类构造函数。这种调用,对于虚基类是合法的,对于非虚基类,是非法的

38.36和37总结:如果有间接虚基类,则除非只需使用该虚基类的默认构造函数,否则必须显式地调用该虚基类的某个构造函数

39.对于MI中的同名方法:对于单继承,如果没有在派生类定义同名方法,则调用时使用最近祖先中的定义;对于多重继承,多个直接祖先都有一个同名方法,因此这个方法名是多义的

40.解决方法:①使用作用域解析运算符来澄清意图,即在调用时显式使用;②重新为派生类定义同名函数,在函数中使用作用域解析运算符调用方法

41.上述40中的方法②存在问题:没有办法访问另一个类中的同名方法所添加的那个类的数据成员。一个方法是,同时调用另一个版本的同名方法来显示完整信息,但这样会造成公共信息冗杂,因此有以下解决方式:①模块化,即提供一个公共信息组件,再在公共基类的派生类(直接基类)中添加自己数据成员的信息组件,最后在派生类的同名方法中将各种组件组合起来,注意方法不能是私有的;②将所有数据组件设置为保护的,不是私有的,通过保护方法(不是数据)将可以更严格控制对数据的访问

42.39-41的总结:在祖先相同时,使用MI必须引入虚基类,并修改构造函数初始化列表的规则;如果在编写类时没有考虑到MI,则要考虑重写它们

43.混合使用虚基类和非虚基类:当类通过多条虚途径和非虚途径继承某个特定的基类时,该类将包含一个表示所有的虚途径的基类子对象和分别表示各条非虚途径的多个基类子对象

44.虚基类和支配:虚基类让同名函数在派生中没有二义性,但是不使用虚基类时,会产生二义性,这种情况下,如果某个名称优先于其他所有名称,则使用时,即使不限定,也不会导致二义性。派生类中的名称优先于直接或间接祖先类的相同名称

045-075 类模板

45.类模板是为了处理行为相同的类,在面临不同类型时,有通用的头文件,而不用定义多个仅仅是数据类型不同的类,的一种泛型编程

46.类模板的定义:template < class Type >;或者使用template < typename Type >,其中class和typename是限定符,但为了和普通类区分,常使用后者,Type通常使用T或Type,也可以用自己的泛型名代替

47.对于模板成员函数替换原有类的雷方法,每个函数头都需要用相同的模板声明打头,并且在使用过程中,所有数据成员、参数列表中的泛型,都应声明为Type或者T,且在定义方法时,需要将限定符从Func::改为Func::,如果在类声明中定义了方法(内联定义),则可以省略模板前缀和类限定符

48.模板的具体实现被称为实例化或具体化,不能将模板成员函数放在独立的实现文件中,由于模板不是函数,不能单独编译,所以它必须和特定的模板实例化请求一起使用。因此通常将所有模板信息放在一个头文件中,并在要使用这些模板的文件中包含头文件

49.仅在程序包含模板并不能生成模板类,必须请求实例化,因此,需要声明一个类型为模板类的对象,方法是使用所需的具体类型替换泛型名,比如:Stack kernels;。对于多组实例化,模板将生成多个独立的类声明和多组独立的类方法,同时允许相同的类型之间赋值,但不允许相同类型的数组赋值

50.可以将内置类型或类对象作为类模板的类型,也可以将指针作为其类型,但是需要对程序做出一定的修改,且使用效果不一定好:如果仅仅提供指针,需要指定大小(实例化),且同一个指针,每次操作的地址都相同,换言之可能程序并不能达到预期效果

51.在类内可以使用缩写,例如Stack在类内可以写为Stack,但是在类外必须写为前者

52.复习:当类内部需要使用动态数组时,应当定义一个析构函数、一个复制构造函数和一个赋值运算符

53.模板常用作容器类,因为类型参数的概念适合于将相同的存储方案用于不同的类型,为容器类提供可重用代码是引入模板的主要动机

54.对于数组模板内使用数组的情况,有两种方式来定义数组,其一是在类中使用动态数组和构造函数参数来提供元素数目,其二是使用模板参数来提供常规数组的大小,array模板使用了后者(多参数模板/非类型参数/表达式参数)

55.在模板中使用的指定特殊类型而不使用泛型名的参数,称为非类型或表达式参数。表达式参数的限制:必须是整型、枚举、引用或指针,因此使用double m不合法,而使用double * rm和double * pm就合法

56.模板代码不能修改参数的值,也不能使用参数的地址,所以在模板中不能使用类似n++或者&n这样的表达(n是表达式参数),实例化模板时,用作表达式参数的值必须是常量表达式

57.54-56两种方法的对比:使用表达式参数时,不用通过new和delete管理堆内存,而是为自动变量维护内存栈,这样在使用很多小型数组时,执行速度将快;缺点是,每种数组大小都将生成自己的模板。构造函数方法需要多写一些代码,但更通用,因为数组大小是作为类成员存储在定义中的,这样可以将一种尺寸的数组赋给另一种尺寸的数组,也可以创建允许数组大小可变的类

58.可以将用于常规类的技术用于模板类,模板类可以用作基类,可以用作组件类,还可以用作其他模板的类型参数,例如可以使用数组模板实现栈模板,使用数组模板来构造数组

59.如果要调用包含多个<>限定符的内容,C++98规定需要在多个</>符号之间使用空格,以避免和<</>>发生冲突,C++11取消了这个限制

60.可以递归使用模板,例如对于array类,其一个方法是array<int, 5>,则可以这样定义一个递归变量:array< array<int, 5>, 10> arrs,这个语句等价于int arrs[10][5]。在模板中,“维”的顺序与二维数组相反

61.模板可以包含多个类型参数,如果希望可以保存两种值,则可以创建并使用Pair模板来保存两个不同的值(标准模板库提供了明为pair的类似模板)

62.类模板的另一项特性:可以为类型参数提供默认值,这时如果省略了那个有默认值的参数,将自动将其设置成默认值。可以为类模板类型参数提供默认值,但不能为函数模板参数提供默认值,然而可以为非类型参数提供默认值,这对于类模板和函数模板都是适用的

63.模板的具体化:和函数模板相似,类模板有隐式实例化、显式实例化和现实具体化,统称为具体化。模板以泛型的方式描述类,而具体化实用具体的类型生成类声明

64.隐式实例化:声明一个或多个对象,指出所需类型,编译器实用通用模板提供的处方生成具体的类定义。编译器在需要对象之前,不会生成类的隐式实例化(只有赋值或实例化之后,才可以生成类定义)

65.显式实例化:实用关键字template并指出所需类型来声明类时,编译器将生成类声明的显式实例化。声明必须位于模板定义所在的名称空间中。如template class array< string, 100>;;在这种情况下,虽然没有创建对象,但编译器将生成类声明(方法定义)

66.显式具体化:如果处理特殊类型,需要与大多数类型不同的处理方式,这时可以使用显式具体化。语法是templat <> class Classname< specialized-type-name> {…};;当具体化模板和通用模板都与实例化请求匹配时,将使用具体化版本

67.部分具体化:部分限制模板的通用性,可以使类型参数之一指定具体的类型。语法是template < class T1> class Pair<T1, int> {…};;这是将T2声明成int,如果指定所有类型,则使用template <> class Pair<int, int> {…};,这将导致显式具体化

68.如果有多个模板可供选择,则编译器将使用具体化程度最高的模板,也可以通过为指针提供特殊版本来部分具体化现有的模板,如果提供的类型不是指针,则编译器将使用通用版本,如果提供的是指针,则编译器将使用指针具体化版本

69.模板可用作结构、类或模板类的成员。可以在模板之外定义模板方法,此时需要先声明模板类,再声明模板类的模板方法,再定义方法,因为模板是嵌套的,使用的语法是template < typename T> template < typename V>,定义还需要指出方法是哪个类的成员,这通过域解析运算符完成

70.模板可以包含类型参数(typename T)和非类型参数(int n),还可以包含本身就是模板的参数,语法是template <template < typename T> class Thing> class Class_Name,模板参数Thing将被替换为声明Class_Name对象时被用作模板参数的模板类型

71.模板类声明也可以有友元,分为三类:①非模板友元;②约束模板友元,即友元的类型取决于类被实例化时的类型;③非约束模板友元,即友元的所有具体化都是类的每一个具体化的友元

72.模板类的非模板友元函数:如果是常规的没有任何参数的友元函数,可以通过访问全局对象、全局指针访问非全局对象、创建自己的对象、访问独立于对象的模板类的静态数据成员来访问类对象;但是在为友元函数提供模板类参数时,需要注意,必须指明模板类参数具体化,可以这样:template class A{ friend void report(A< T> &); …};;这样将生成具体化,意味着不同的report()是重载版本,report()本身不是模板函数,而只是使用一个模板作参数

73.模板类的约束模板友元函数:友元函数本身成为模板。要使类的每一个具体化都获得与友元匹配的具体化。语法是:①在类定义前面声明每个模板函数template < typename T> void couts(); template < typename T> void report(T &);;②然后在函数种再次将模板声明为友元:template < typename TT> class A{… friend void counts< TT>(); friend void report<>(A< T> &);};;对于count(),在使用时必须使用< TT>指出具体化,而report(T &)则可以使用report<>(A< TT> &)这样的方式来具体化

74.模板类的非约束模板友元函数:通过在类内部声明模板,可以创建非约束友元函数,即每个函数具体化都是每个类具体化的友元,对于非约束友元,友元模板类型参数与模板类类型参数是不同的

75.模板别名(C++11):可以使用typedef为模板具体化指定别名,例如typedef std::array<double, 12> arrd;;则可以使用arrd gallons来声明一个变量。对于C++11,有另一种方法:template < typename T> using arrtype=std::array<T,12>;,这样可以使用arrtype< double> gallons来声明,也就是arrtype< T>表示std::array<T.12>。后续还有“可变参数模板”,即可接受可变数量的参数

076-085 友元

76.类并非只能拥有友元函数,也可以将类作为友元。在这种情况下,友元类的所有方法都可以访问原始类的私有成员和保护成员;也可以做更严格的限制,只将特定的成员函数指定为另一个类的友元。哪些函数、成员函数或类作为友元是由类定义的,不能从外部强加友情

77.当某类东西可以改变另一类东西的状态,但又不互相包含、互相等同时,可以认为是友元类,比如电视和遥控。因为电视的基本功能大于遥控,所以可以将遥控类定义为电视类的友元类

78.友元声明可以位于公有、私有或保护部分,这并无紧要,但是需要注意的是,需要先声明定义基本类,再声明定义友元类。

79.现在使用双状态参数,表示某一个量的两个状态时(true和false表示),可以使用按位运算符或按位异或和赋值运算符^=来表达

80.使用类友元的感受:需要在使用友元类的类中声明友元类,且这个类在友元类的声明之前;在友元类中进行定义时,多使用内联定义方式,且在友元类成员函数中,需要包含原本类的方法,以完成对原本类方法的访问

81.友元成员函数:可以只将类的某个方法声明为友元成员函数,即此时,电视类的某个方法声明为友元函数,且此友元函数调用了Remote类的方法,而Remote类的方法又需要电视类作为参数,因此它们互相为前提。所以需要使用“前向声明”

82.将电视类首先声明,再声明遥控类,而将遥控类中需要电视类作为参数的方法,只声明不定义(因为遥控类需要电视类的完整定义,需要调用电视类的一些方法,但这样不是默认内联函数),之后定义电视类,并将其中的方法写完后,再将没有定义的遥控类方法显式地定义为内联函数

83.内联函数的链接性是内部的,这意味着函数定义必须在使用函数的文件中,当内联定义位于头文件中,则包含头文件可确保将定义放在正确的地方。也可以将定义放在实现文件中,但必须删除inline关键字,这样函数的链接性是外部的

84.当两个类中,互相有方法作为另一个类的友元成员函数时,需要注意类声明与类方法定义的顺序,以确保编译器有足够的信息来编译方法,使用方法

85.需要访问两个类的私有数据时,有两种方法:将一个方法定义为类的成员函数,同时它是另一个类的友元;另一种方法是将函数作为两个类的友元,需要分别在两个类内声明相同的函数,参数相互对称。同时,需要使用前向声明

086-090 嵌套类

86.在另一个类中声明的类被称为嵌套类,通过提供新的类型类作用域来避免名称混乱。包含类的成员函数可以创建和使用被嵌套类的对象,而仅当声明位于公有部分,才能在包含类的外面使用嵌套类,而且必须用作用域解析运算符

87.对类进行嵌套与包含并不同,包含意味着将类对象作为另一个类的成员,而对类进行嵌套不创建类成员,而是定义了一种类型,该类型仅在包含嵌套类声明的类中有效

88.嵌套类的声明位置决定了嵌套类的作用域,它决定了程序的哪些部分可以创建这种类的对象。嵌套类也有公有部分、保护部分和私有部分,控制了对类成员的访问。在哪些地方可以使用嵌套类以及如何使用嵌套类,取决于作用域和访问控制

89.①嵌套类在另一个类的私有部分声明,则只有另一个类知道它;②嵌套类在另一个类的保护部分声明,则它对于另一个类来说是可见的,但是对于外部是不可见的;③嵌套类在另一个类的公有部分声明,则允许另一个类、另一个类的派生类和外部使用它,因为是公有的。但由于嵌套类的作用域为包含它的类,因此在外部使用时,必须使用类限定符

90.也可以使用包含嵌套类的模板,将模板中的待定类型也使用相应的标记符(Type或T)即可

091-100 异常

91.对于被0除的情况,很多编译器通过生成一个表示无穷大的特殊浮点值来处理,cout将这种值显示为inf/Inf/INF或类似的内容。而有些老式编译器,在发生这种情况时程序会发生崩溃,因此最好写在所有系统上都相同的受控方式运行的代码

92.对于异常的处理方式,可以使用abort()函数,abort()函数位于cstdlib头文件中,典型实现是向标准错误流(cerr使用的错误流)发送消息abnormal program temination(程序异常终止),然后终止程序。它还返回一个随实现而异的值,告诉操作系统(父进程)处理失败,其是否刷新缓冲区取决于具体实现

93.如果想要结束进程,也可以使用exit(),该函数刷新文件缓冲区,但不显示消息

94.在程序中调用abort(),函数将直接终止程序,而不是先返回到main(),一般而言,现实的程序异常中断消息随编译器而异,人为地可以在调用可能出错的函数时,使用abort()函数,但这样通常不安全

95.一种比异常终止更灵活的方法是返回错误码。例如ostream类get()返回下一个输入字符的ASCII码,但到达文件尾时,返回EOF。对于一般的程序,可以通过返回bool值以及指针地址来表达程序是否顺利运行,而通过其他方式传递参数(指针或引用或全局变量)

96.通过返回错误码的方式,可以让程序不异常终止,这样更符合人机交互的目的。但设计需要依靠用户检查函数的返回值,一般是比较难做到的

97.异常机制:异常提供了将控制权从程序的一个部分传递到另一个部分的途径,对异常的处理有3个组成部分:①引发异常;②使用处理程序捕获异常;③使用try块

98.程序在出现问题时将引发异常,throw语句是跳转,命令程序跳转到另一条语句。throw关键字表示引发异常,紧随其后的值(字符串或对象)指出了异常的特征

99.程序使用异常处理程序(exception handler)来捕获异常,异常处理程序位于要处理问题的程序中,catch关键字表示捕获异常。处理程序以关键字catch开头,随后是位于括号中的类型声明,它指出了异常处理程序要响应的异常类型;然后是一个用花括号括起的代码块,指出采取的措施。catch关键字和异常类型用作标签,指出当异常被引发时,跳转到这个位置执行。异常处理程序也称为catch块

100.try块标识其中特定的异常可能被激活的代码块,它后面跟一个或多个catch块。try块由关键字try指示,关键字try后面是一个由或括号括起的代码块,表明需要注意这些代码引发的异常

说明:模板类的约束模板友元函数和模板类的非约束模板友元函数,我也弄得不是很清楚,算是个盲点,以后学习了再补充,现在只是把书上的内容摘抄了一下。希望快快好起来

下期预告:异常、RTTI、输入、输出

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值