day06 c++核心编程(2)自我总结版

目录

对象的初始化和清理

构造函数和析构函数

 析构函数语法: ~类名(){}

构造函数的分类及调用

拷贝构造函数的调用时机

使用一个已经创建完毕的对象来初始化一个新对象

值传递的方式给函数参数传值 ​编辑

构造函数调用规则

 深拷贝与浅拷贝(重点)

初始化列表


对象的初始化和清理

简单来说,对象构建的函数,根据需求可能会在内存区开辟空间,存储数据,如果不进行清理,那么下一次打开,就可能通过其他意外直接看到这些数据。

就像出售自己的二手手机,如果出售,在出售前必定清除数据,保证数据不会被其他人看到。

初始化和清理是必须做,可以由系统编译器去做,但是自己写了,就有限运行自己写的清理函数。

构造函数和析构函数

对象的初始化和清理也是两个非常重要的安全问题

一个对象或者变量没有初始状态,对其使用后果是未知

同样的使用完一个对象或变量,没有及时清理,也会造成一定的安全问题

c++利用了构造函数析构函数解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作。

对象的初始化和清理工作是编译器强制要我们做的事情,因此如果我们不提供构造和析构,编译器会提供

请注意,编译器提供的构造函数和析构函数是空实现。

  • 构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。

  • 析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。

构造函数语法:类名(){}

  1. 构造函数,没有返回值也不写void

  2. 函数名称与类名相同

  3. 构造函数可以有参数,因此可以发生重载

  4. 程序在调用对象时候会自动调用构造,无须手动调用,而且只会调用一次

class person
{
public://需要在公共权限下,才能访问到,这是因为class默认为私有权限,private
	//1.1、构造函数
	// 没有返回值,不用谢void
	// 函数名 与类名相同
	// 构造函数可以有参数,可以发生重载
	// 创建对象的时候,构造函数会自动调用,而且只调用一次。
	//构造函数语法:   类名(){}
	person()
	{

		cout << "person构造函数的调用" << endl;
	}

};


void test01()
{
	person p;//请注意,这里只是调用了一个对象person的变量,未做其任何动作。
}

 请注意,只是调用了一个对象person的变量,未做其任何动作,依旧成功输出了构造函数,说明,构造函数在c++内是自动调用的。

 析构函数语法: ~类名(){}

  1. 析构函数,没有返回值也不写void

  2. 函数名称与类名相同,在名称前加上符号 ~

  3. 析构函数不可以有参数,因此不可以发生重载

  4. 程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次

~person()
	{
		cout << "~person析构函数的调用" << endl;
	}

同上,我们可以发现,只要调用person里的数据,就必定运行构造函数和析构函数。

如果系统的编写,往往是一个空实现,但是自己编写,则更加彻底和清晰。

总结:

1、构造函数相当于初始化,析构函数就是把class内数据删除干净,防止数据错误。

2、析构函数和构造函数,自己不写,系统提供,但不显示。

构造函数的分类及调用

两种分类方式:

按参数分为: 有参构造和无参构造

按类型分为: 普通构造和拷贝构造

有参构造和无参构造

class person
{
public://需要在公共权限下,才能访问到,这是因为class默认为私有权限,private
	//构造函数
	person()//无参
	{
		cout << "person的   无参  构造函数的调用" << endl;
	}
	person( int a)//有参
	{
		cout << "person的  有参  构造函数的调用" << endl;
	}

};

拷贝构造函数
拷贝构造

//拷贝构造函数
	//普通构造和拷贝构造
	person(const person  &p)//请注意,const禁止修改,并使用引用的写法,这是固定写法
	{
		age = p.age;
		cout << "拷贝构造函数!" << endl;
	}

 其他的都是普通构造

三种调用方式:

括号法

void test01() //括号法调用
{
	person p;//请注意,这里只是调用了一个对象person的变量,未做其任何动作。
	person p2(10);//有参构造函数调用
	person p3(p2);
	//注意事项:调用默认构造函数的时候,不要加()
	//因为语法规定,编译器认为这是一个函数的声明

	cout << "p2的年龄为:: " << p2.age << endl;  //给函数做了一个初始化
	cout << "p3的年龄为:: " << p3.age << endl;   //拷贝p2的数据
}

显示法

	//显示法
	person p1;
	person p2 = person(10);
	person p3 = person(p2);
	//相较于括号调用,显示调用就是明显显示其关系,更加直观
	person(10);//匿名对象 特点:当前执行结束后,系统会理解回收匿名对象
	//匿名函数被调用后会被立即析构。

	//不要利用拷贝构造函数,初始化匿名对象,会报错,因为编译器认为这是一个声明

隐式转换法

	//隐式转换发
	person p4 = 10; // 编译器自动给你转换成 person p4 = Person(10); 
	person p5 = p4; //                    Person p5 = Person(p4); 

 本小节代码

#include <iostream>
#include<string>
using namespace std;
//1、构造函数的分类及调用
//分类
//按照参数分类 无参构造和有参
class person
{
public:

	//构造函数
	person()//无参
	{
		cout << "person的   无参  构造函数的调用" << endl;
	}
	person( int a)//有参
	{
		age = a;
		cout << "person的  有参  构造函数的调用" << endl;
	}
	//按照参数分类 无参构造和有参
	//拷贝构造函数
	//普通构造和拷贝构造
	person(const person  &p)//请注意,const禁止修改,并使用引用的写法,这是固定写法
	{
		age = p.age;
		cout << "拷贝构造函数!" << endl;
	}

public://需要在公共权限下,才能访问到,这是因为class默认为私有权限,private
	int age;

};


void test01() 
{
	//括号法调用
	
	//person p;//请注意,这里只是调用了一个对象person的变量,未做其任何动作。
	//person p2(10);//有参构造函数调用
	//person p3(p2);
	注意事项:调用默认构造函数的时候,不要加()
	因为语法规定,编译器认为这是一个函数的声明

	//cout << "p2的年龄为:: " << p2.age << endl;  //给函数做了一个初始化
	//cout << "p3的年龄为:: " << p3.age << endl;   //拷贝p2的数据

	//显示法
	//person p1;
	//person p2 = person(10);
	//person p3 = person(p2);
	相较于括号调用,显示调用就是明显显示其关系,更加直观
	//person(10);//匿名对象 特点:当前执行结束后,系统会理解回收匿名对象
	匿名函数被调用后会被立即析构。

	不要利用拷贝构造函数,初始化匿名对象,会报错,因为编译器认为这是一个声明

	//隐式转换发
	person p4 = 10; // 编译器自动给你转换成 person p4 = Person(10); 
	person p5 = p4; //                    person p5 = Person(p4); 
}

int main()
{
	test01();

	system("pause");

	return 0;
}

拷贝构造函数的调用时机

C++中拷贝构造函数调用时机通常有三种情况

  • 使用一个已经创建完毕的对象来初始化一个新对象

  • 值传递的方式给函数参数传值

  • 以值方式返回局部对象

使用一个已经创建完毕的对象来初始化一个新对象

 效果如图,可以调用p2,直接使用p1的数据。

代码

class person
{
public:
	person()
	{
		cout << "person的默认构造函数调用 " << endl;
	}
	person(int age)
	{
		cout << "person的有参构造函数调用 " << endl;
		m_age = age;
	}
	person(const person & p)     //拷贝构造函数
	{
		cout << "person的默认构造函数调用 " << endl;
		m_age = p.m_age; 
		//注意用法,这里是将m_age=p.m_age,
		//相当于使用一个已经创建完毕的对象来初始化一个新对象
	}
	~person()
	{
		cout << "person的析构函数调用 " << endl;
	}

	int m_age;
};
void test01()
{
	person p1(20);
	person p2(p1);  //括号法调用拷贝构造函数
	cout << "p2的年龄为: " << p2.m_age << endl;
}

int main()
{
	test01();

	system("pause");
	return 0;
}

值传递的方式给函数参数传值

 

 请注意,代码段并不完整,不要盲目粘贴

//2、值传递的方式给函数参数传值
void do_work(person p)//值传递,没有&,看清楚啊
{

}
void test02()
{
	person p;
	do_work(p);
}

int main()
{
	//test01();
	test02();

 以值方式返回局部对象

 

//3、以值方式返回局部对象
person do_work2()
{
	person p1;   //调用person数据
	return p1;  //再返回p1
}
void test03()
{
	person p = do_work2();//吧p赋值为do_work2

}

int main()
{
	//test01();
	//test02();
	test03();

本节 完整代码

#include <iostream>
#include<string>

using namespace std;
//C++中拷贝构造函数调用时机通常有三种情况
//
//* 使用一个已经创建完毕的对象来初始化一个新对象
//* 值传递的方式给函数参数传值
//* 以值方式返回局部对象

class person
{
public:
	person()
	{
		cout << "person的默认构造函数调用 " << endl;
	}
	person(int age)
	{
		cout << "person的有参构造函数调用 " << endl;
		m_age = age;
	}
	person(const person & p)     //拷贝构造函数
	{
		cout << "person的默认构造函数调用 " << endl;
		m_age = p.m_age; 
		//注意用法,这里是将m_age=p.m_age,
		//相当于使用一个已经创建完毕的对象来初始化一个新对象
	}
	~person()
	{
		cout << "person的析构函数调用 " << endl;
	}

	int m_age;
};
void test01()
{
	person p1(20);
	person p2(p1);  //括号法调用拷贝构造函数
	cout << "p2的年龄为: " << p2.m_age << endl;
}

//2、值传递的方式给函数参数传值
void do_work(person p)//值传递,没有&,看清楚啊
{

}
void test02()
{
	person p;
	do_work(p);
}

//3、以值方式返回局部对象
person do_work2()
{
	person p1;   //调用person数据
	return p1;  //再返回p1
}
void test03()
{
	person p = do_work2();//吧p赋值为do_work2

}

int main()
{
	//test01();
	//test02();
	test03();
	system("pause");

	return 0;
}

构造函数调用规则

只要写了类,必定会有默认构造函数、拷贝构造函数、析构函数

你可以不写,系统提供,你写了,系统就用你的,不会提供别的构造函数。

默认情况下,c++编译器至少给一个类添加3个函数

1.默认构造函数(无参,函数体为空)

2.默认析构函数(无参,函数体为空)

3.默认拷贝构造函数,对属性进行值拷贝

构造函数调用规则如下:

  • 如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造

  • 如果用户定义拷贝构造函数,c++不会再提供其他构造函数

 无参构造拷贝

 有参构造

 

 不太理解,但是规则就是这么个回事

如果用户提供有参构造,编译器不会提供默认构造,会提供拷贝构造

如果用户提供拷贝构造,编译器不会提供其他构造函数

我不认为这是重点,但这是一个语法的运用规则

#include <iostream>
#include<string>
using namespace std;

//构造函数调用规则
//默认情况下,c++编译器至少给一个类添加3个函数
//1.默认构造函数(无参,函数体为空)
//2.默认析构函数(无参,函数体为空)
//3.默认拷贝构造函数,对属性进行值拷贝

class person
{
public:
	person()
	{
		cout << "person的无——参构造函数" << endl;
	}
	person(int age)
	{
		m_age = age;
		cout << "person的有——参构造函数" << endl;
	}
	person(const person& p)
	{
		m_age = p.m_age;
		cout << "person的拷贝构造函数" << endl;
	}
	~person()
	{
		cout << "person的析构构造函数" << endl;
	}
	int m_age;
};

//void test01()
//{
//	person p;
//	p.m_age = 18;
//
//	person p2(p);//拷贝构造的用法
//	cout << "p2的年龄为: " << p2.m_age << endl;
//}

void  test02()
{
	//如果用户提供有参构造,编译器不会提供默认构造,会提供拷贝构造
	person p1; //此时如果用户自己没有提供默认构造,会出错
	person p2(10); //用户提供的有参
	person p3(p2); //此时如果用户没有提供拷贝构造,编译器会提供

		//如果用户提供拷贝构造,编译器不会提供其他构造函数
	person p4; //此时如果用户自己没有提供默认构造,会出错
	person p5(10); //此时如果用户自己没有提供有参,会出错
	person p6(p5); //用户自己提供拷贝构造
	
}

int main()
{
	test02();

	return 0;
}

 深拷贝与浅拷贝(重点)

这里是个面试重点,因为这是坑

浅拷贝:简单的赋值拷贝操作

深拷贝:在堆区重新申请空间,进行拷贝操作 

 这里就是浅拷贝

 

 深拷贝和浅拷贝区别是,深拷贝的数据在堆区,只能有程序员手动编写代码释放。

这里就体现析构函数的用法。

这里是用一个判断,首先判断,m_high是否为空,为空就释放,但是p2是拷贝p1的,内存地址一致,因此第一遍将p1释放后,在释放p2,可是内存地址已经不在了,就出现了错误。

 于是要我们自己写拷贝函数中的深拷贝操作,来解决浅拷贝带来的重复释放堆区问题

 完整代码

#include <iostream>
#include<string>
using namespace std;

//浅拷贝:简单的赋值拷贝操作

//深拷贝:在堆区重新申请空间,进行拷贝操作

class person
{
public:
	person()
	{
		cout << "person的默认构造函数调用" << endl;
	}
	person(int age,int high)
	{
		m_age = age;
		m_high =  new int(high);
		//利用new,把high指向堆区,返回一个int* ,然后用一个int*去接受
		cout << "person的有参构造函数调用" << endl;
	}
	//自己写一个拷贝构造函数,解决浅拷贝带来的问题
	person(const person& p)
	{
		cout << "拷贝构造函数!" << endl;
		//如果不利用深拷贝在堆区创建新内存,会导致浅拷贝带来的重复释放堆区问题
		m_age = p.m_age;
		//m_high=p.m_high 就是编译器默认实现的代码
		
		//因此我们需要自己写拷贝函数中的深拷贝操作
		m_high = new int(*p.m_high);
	}
	//~person()
	//{
	//	cout << "person的析构函数调用" << endl;
	//}
		//析构函数
	~person() {
		cout << "析构函数!" << endl;
		if (m_high != NULL)
		{
			delete m_high;
		}
	}
	int m_age;
	//注意,整活了,使用指针,把身高的数据指向一片空间,堆区
	int * m_high;
};
void test01()
{
	person p1(18,180);
	cout << "p1的年龄为: " << p1.m_age <<"身高为: "<<*p1.m_high << endl;
	person p2(p1);
	cout << "p2的年龄为: " << p2.m_age << "身高为: " <<* p2.m_high << endl;
}
  

int main()
{
	test01();

	return 0;
}

 如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题

初始化列表

C++提供了初始化列表语法,用来初始化属性

语法:构造函数():属性1(值1),属性2(值2)... {}

构造函数就是一种初始化方法,同时有另外一种更为简单的初始化,初始化列表

 相较于传统的,列表耿局简单,明了的特点。

class person
{
public:
//传统版
//person(int a, int b, int c)
//{
//	m_a = a;
//	m_b = b;
//	m_c = c;
//}

//列表版
//初始化列表方式初始化
person(int a, int b, int c) :m_a(a), m_b(b), m_c(c) {}

void PrintPerson() 
{
	cout << "mA:" << m_a << endl;
	cout << "mB:" << m_b << endl;
	cout << "mC:" << m_c << endl;
}
private:
int m_a;
int m_b;
int m_c;
	
};
void test01()
{

person p1(18,20,24);
//传统版 ,在public:权限下
/*cout << "m_a=" << p1.m_a << "  " << "m_b=" << p1.m_b 
	<< "  " << "m_c=" << p1.m_c<< endl;*/
//列表版,加上private的
p1.PrintPerson();

}
 int main()
{
test01();

return 0;
}

类对象作为类成员

C++类中的成员可以是另一个类的对象,我们称该成员为 对象成员

简单理解就是嵌套,和struct一样的,套娃。但是要注意权限的问题,class最麻烦的就是权限之间的问题。

练习

 要注意变量名,建议纸上画好关系图,再来命名,不然容易蒙。

当其他类的成员为本类成员,优先构造其他类的对象,在构造自身

 析构则相反,

#include <iostream>
#include<string>
using namespace std;


class phone //手机类
{
public:
	phone(string p_name)
	{
		m_p_name = p_name;
	}
	//品牌名称
	string m_p_name;
};
class user  //用户
{
public:
	//phoen m_p_name=p_name .隐式转换法,有系统代替完成

	user(string m_uname,string p_name ):m_user_name(m_uname),m_phone(p_name)
	{
		cout << "user的构造函数调用" << endl;
	}
	~user()
	{
		cout << "user的析构函数调用" << endl;
	}

	void play_phone()
	{
		cout << m_user_name << " 使用" << m_phone.m_p_name << " 牌手机! " << endl;
	}
	//姓名
	string m_user_name;
	//手机
	phone m_phone;
};
void test01()
{
	user u("张三", "华为");
	u.play_phone();
}
 int main()
{
	 test01();

return 0;
}

静态成员

静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员

静态成员分为:静态成员变量   静态成员函数

  • 静态成员变量

    • 所有对象共享同一份数据

    • 在编译阶段分配内存

    • 类内声明,类外初始化

  • 静态成员函数

    • 所有对象共享同一个函数

    • 静态成员函数只能访问静态成员变量

静态成员变量

  • 所有对象共享同一份数据

  • 在编译阶段分配内存

  • 类内声明,类外初始化

 

 

 在类外初始化后就可以正常调用了

要加上域,告诉编译器属于谁

 重新对静态成员变量赋值之后,还是打印p1,就会发现值变了。

静态成员变量,不属于某个对象上,所有对象都共享同一份数据
  因此静态成员变量有两种访问方式

 补充:类外不可以访问私有权限,仅能通过公共权限的接口 

静态成员函数

大体上和静态变量一致。

两种访问方式

 请注意,静态函数只能访问静态变量

如图

 正确用法。

 不可以访问的原因是因为无法区别变量是那个对象的,置多个或嵌套的对象

#include <iostream>
#include<string>
using namespace std;

class person
{
public:

特性1:所有对象都共享一份数据,
//特性2:编译阶段就分配了内存
//	类内声明,类外初始化,就像int a;


	//静态函数
	//与静态变量,一样
	static void func()
	{
		m_a = 100;
		//m_b = 200;非静态,禁止访问,
		//不可以访问的原因是因为无法区别那个对象的,置多个或嵌套的对象
		cout << "static void func静态函数调用" << endl;
	}
	static int m_a;
	int m_b;
};
//类外初始化
 int person::m_a;
 //要加上域,告诉编译器属于谁

void  test01()
{
	//静态成员变量,不属于某个对象上,所有对象都共享同一份数据
	//因此静态成员变量有两种访问方式
	//1、通过对象访问
	//person p1;
	//p1.m_a = 10;
	//cout << p1.m_a << endl;
	//person p2;
	//p2.m_a = 200;  //给静态成员变量重新赋值后
	//cout << p1.m_a << endl;
	//2、通过类名访问
	cout << person::m_a << endl;

	//静态函数的调用和静态变量一致,都是通过两种方式,1,、对象访问,2、类名访问

	//1 、对象访问
	person p3;
	p3.func();

	//2、类名访问
	person::func();

}
 int main()
{
	 test01();
return 0;
}

C++对象模型和this指针

成员变量和成员函数分开存储

在C++中,类内的成员变量和成员函数分开存储

只有非静态成员变量才属于类的对象上

class Person {
public:
	Person() 
        //空对象会占用内存空间: 1
        //c++编译器会给每一个空对象分配一个自己空间,是为了区分空对象
        //每个空对象都有独特的内存空间。 
    {
		mA = 0;
	}
	//非静态成员变量占对象空间
	int mA;
	//静态成员变量不占对象空间
	static int mB; 
	//函数也不占对象空间,所有函数共享一个函数实例
	void func() {
		cout << "mA:" << this->mA << endl;
	}
	//静态成员函数也不占对象空间
	static void sfunc() {
	}
};

int main() {

	cout << sizeof(Person) << endl;

	system("pause");

	return 0;
}

没什么好讲的,这一部分更多讲原理,方便下面内容的理解,重点就一句,类内成员变量和成员函数是分开存储的,这导致为了提高算法效率,要注意程序的设计。

this指针概念

建议先读一段话,重点理解一下内容。

有点抽象,需要图解。

C++中成员变量和成员函数是分开存储的

每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码

那么问题是:这一块代码是如何区分那个对象调用自己的呢?

 

c++通过提供特殊的对象指针,this指针,解决上述问题。this指针指向被调用的成员函数所属的对象

this指针是隐含每一个非静态成员函数内的一种指针

this指针不需要定义,直接使用即可

this指针的用途:

  • 当形参和成员变量同名时,可用this指针来区分

  • 在类的非静态成员函数中返回对象本身,可使用return *this

·

 但是不建议这么干,同样的变量名他人阅读的时候脑袋会炸的,多敲敲键盘。

 这就使得,p2可以反复多次调用p1.

这种就是链式编程思想

#include <iostream>
#include<string>
using namespace std;

class Person
{
public:

	Person(int age)
	{
		//1、当形参和成员变量同名时,可用this指针来区分
		this->age = age;
	}

	Person& PersonAddPerson(Person p)
	{
		this->age += p.age;
		//返回对象本身,对象本身是int,还返回int,这是个重点
		return *this;
	}

	int age;
};

void test01()
{
	Person p1(10);
	cout << "p1.age = " << p1.age << endl;

	Person p2(10);
	//在第一次调用返回的是void,因此下一次重复调用,不符合调用要求。拒绝调用
	p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1);
	cout << "p2.age = " << p2.age << endl;
}

int main() {

	test01();

	system("pause");

	return 0;
}

空指针访问成员函数

C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针

如果用到this指针,需要加以判断保证代码的健壮性

本节重点是思想,尽量避免程序的bug

 

 看到这里就知道哪里出了问题。

 关闭p->show_person_age();,则能正常运转。很奇妙

 有点好奇,,有必要每个指针都要写一个if来保证嘛?

class person
{
public:
	void print_class_name()
	{
		cout << "person class doing" << endl;
}
	void show_person_age()
	{
		//处理办法
		if (this == NULL)
		{
			return;//给返回,就不会到下面一行了
		}
		cout << "person年龄age= " <<this-> m_age << endl;
	}
	int m_age;
};

void test01()
{
	person* p = NULL;//因这里的指针已经指向了空,所以在调用时就报错了、
	p->print_class_name();
	p->show_person_age();
}
int main() 
{
	test01();
	system("pause");
	return 0;
}

const修饰成员函数

常函数:

  • 成员函数后加const后我们称为这个函数为常函数

  • 常函数内不可以修改成员属性

  • 成员属性声明时加关键字mutable后,在常函数中依然可以修改

就是简单的锁定,但要知道用法

但是加上const后

 

 

 

 

 在语法上,在成员变量后加上const后,就禁止修改了

但是加上mutable就是可以修改的

class person
{
public:
	//this指针的本质,是指针常量,指针的指向是不可以修改的
	//const person *const this   ---双重锁定,值也不可以修改
	//在成员函数后面加上const ,修饰的是this指向,让指向的值也不可以修改
	void show_person() const
	{
		//m_a = 100;//加上const 后不可以修改
		//this->m_a = 100;//指针也不可以修改
		//this = NULL;//this指针不可以修改真挚的指定向
		this->m_b = 100;
	}
	int m_a;
  mutable  int m_b;//特殊变量,即使在常量函数中,也可以修改这个值,
  //用法,在数据类型前加上,mutable
};

 

 

常对象:

  • 声明对象前加const称该对象为常对象

  • 常对象只能调用常函数

 请注意,常函数不可以调用普通成员函数,因为普通成员函数可以修改属性。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值