C++ STL :Vector内存分配与释放

vector 可以容纳许多类型的数据,如若干个整数,所以称其为容器。

vector 是C++ STL的一个重要成员,使用它时需要包含头文件:#include<vector>

关于vector的使用,虽然可以动态的分配内存,但是稍不注意,就会落入内存陷阱中,无形中增大了程序的内存开销,导致程序崩溃。基于此,有必要梳理一下C++ STL中的vector的内存分配与释放机制。

文章从“定义”、“添加”、“清空”三个部分来探究vector的内存分配和释放机制。

导读:

1. vector内存相关介绍

  • size(),capacity(),push_back(),reserve(),resize(),clear(),swap()
  • vector内存增长特点

2. vector定义与内存分配

  • 类中定义 & 类外定义(包括全局、主函数、类成员函数、自定义函数等)
  • 一维vector & 二维vector
  • 空vector & 指定行vector & 指定行列vector

3. vector定义方式建议

4. vector清空元素与内存释放

  • clear()
  • swap()
以下是正文,测试代码会放在最后。

1. vector内存相关介绍

1.1 相关函数

(1)b.size():容器当前拥有的元素个数。

(2)b.capacity():容器在必须分配新存储空间之前可以存储的元素总数。

区别:创建完空vector后,其size和capacity都为0,但是向vector插入元素后,会发生变化,通常capacity大于等于size,这是vector内存增长机制决定的。

(3)b.push_back():在向量最后插入一个元素。在调用push_back时,若当前容量已经不能够放入新的元素(capacity=size),那么vector会重新申请一块内存,把之前的内存里的元素拷贝到新的内存当中,然后把push_back的元素拷贝到新的内存中,最后要析构原有的vector并释放原有的内存。所以说这个过程的效率是极低的,为了避免频繁的分配内存,C++每次申请内存都会成倍的增长。

(4)b.reserve(a):容器预留空间a,但在空间内不真正创建元素对象(capacity=a,无size)。所以在没有添加新的对象之前,不能引用容器内的元素。加入元素时,要调用push_back()/insert()函数,值得一提的是,若添加元素没有超出预留,那么是不会对内存进行重新分配的。此外,若加入元素超出了reserve()的值,是可以继续添加的,但是此时就会触发push_back()的空间预留机制。

(5)b.resize(a,0):改变容器的大小,且创建对象(指定或默认0,初始capacity = size = a)。因此,调用这个函数之后,可以引用容器内的对象了,因此当加入新的元素时,用operator[]操作符,或者用迭代器来引用元素对象。此时再调用push_back()函数,是加在这个新的空间后面的。同样也会触发push_back()的空间预留机制。

容器类型提供resize操作来改变容器所包含的元素个数:如果当前的容器长度大于新的长度值,则该容器后部的元素会被删除;如果当前的容器长度小于新的长度值,则系统会在该容器后部添加新元素。

需要注意的是:resize操作可能会使迭代器失效,对于所有的容器类型,如果resize操作压缩了容器,则指向已删除的元素的迭代器失效。

区别:

  • reserve函数只有一个参数,即需要预留的容器的空间;resize函数可以有两个参数,第一个参数是容器新的大小, 第二个参数是要加入容器中的新元素,如果这个参数被省略,那么就调用元素对象的默认构造函数。
  • 当不超过预留空间时,reserve()不涉及内存的重新分配,resize()会涉及内存的重新分配。但是如果是对空容器操作,那么二者看不出内在的区别。
  • reserve()只修改capacity大小,不修改size大小,
  • resize()既修改capacity大小,也修改size大小。

(6)b.clear():清空向量中的元素。但是即使clear(),vector所占用的内存空间依然如故,无法保证内存的回收。一维vector.clear(),清空一维的元素,但是仍旧保留着列的capacity;二维vector.clear(),清空各行的列,并且回收列内存capacity,但是保留行的capacity

(7)a.swap(b):将a中的元素和b中的元素进行整体性交换。除此之外,①可以利用swap()方法去除vector多余的容量:vector<T>(x).swap(x);其中,x是当前要操作的容器,T是容器的类型。②利用swap()方法清空vector容器:当 swap() 成员方法用于清空 vector 容器时,可以套用如下的语法格式:vector<T>().swap(x)

1.2 vector内存增长策略和特点

(1)vector的容器的大小只可以增加,不可以减少。当我们使用push_back() , insert() , emplace()等成员方法的时候,有可能会增加容量,但是我们使用 pop_back()、erase()、clear() 等方式的时候,并不会减少实际 的内存容量。只是可以删除容器里面的内容。

(2)vector 有一个机制是这样的,如果新加入一个元素,比如通过push_back(),但是size 大于了capacity,那么vector 就会重新找一块更大的地方再把数据放进去。重新分配的过程:申请一块新的内存 > 拷贝数据 > 释放原内存

所以,使用vector容器的时候可以预先空间,把capacity定得够大,这样可以尽量避免重新分配vector 的内存,不增加程序的负担。即:reserve()预留空间+push_back()压入元素+size()控制读取 的策略,但是还是不推荐这么用,原因后面解释。

(3)综合来看,vector为了支持快速的随机访问,vector容器的元素以连续方式存放,每一个元素都紧挨着前一个元素存储。设想一下,当vector添加一个元素时,为了满足连续存放这个特性,都需要重新分配空间、拷贝元素、撤销旧空间,这样性能难以接受。因此STL实现者在对vector进行内存分配时,其实际分配的容量要比当前所需的空间多一些。就是说,vector容器预留了一些额外的存储区,用于存放新添加的元素,这样就不必为每个新元素重新分配整个容器的内存空间。

在调用push_back时,每次执行push_back操作,相当于底层的数组实现要重新分配大小;这种实现体现到vector实现就是每当push_back一个元素,都要重新分配一个大一个元素的存储,然后将原来的元素拷贝到新的存储,之后在拷贝push_back的元素,最后要析构原有的vector并释放原有的内存。

2. vector定义与内存分配

2.1 类中定义 & 类外定义

vector在类中私有/公有成员中的定义方式居然和全局、函数内的定义方式还有所区别,具体原因也没了解到(和默认构造函数有关),总之用这玩意儿到处都是坑,吐。

(1)类外定义(圆括号)

一维vector

vector<int>a(10);

二维vector

vector<vector<int>>c(5, vector<int>(10));

以上定义都是有初始值的,所以可以直接用下标访问。举例的是已知一些行列信息的定义方式,其余的定义方式在下面说,差不多。

(2)类中定义{花括号}

不能使用以上类外的两种定义方式。

一维vector:无法指定列长,可以用花括号{ }初始化列元素。

vector<int>a1{ 1,2,3 };

二维vector:可以用花括号{ }指定行数,无法指定列长,但可以用花括号{ }初始化列元素。

vector<vector<int>>c{ N };
vector<vector<int>>d1{ N ,vector<int> {1,2,3} };

只要vector中没有定义列长,那么就是个空的容器。具体细节见下代码:

class Vector_define
{
private:
	int i, j;
	/*一维vector*/
	//vector<int>a(N);				/*在自定义类中无法对vector实现圆括号()初始化长度的定义*/
	vector<int>a{ N };				/*此种花括号{}的初始化可以定义。作用:只是输入一个元素,而非定义长度*/
	vector<int>a1{ 1,2,3 };			/*支持直接输入元素*/
	vector<int>b;					/*正常定义,空的,无长度*/
	
	/*二维vector*/
	//vector<vector<int>>c(N, vector<int>(T));			/*该种定义同一维,无效定义*/
	vector<vector<int>>c{ N };							/*二维vector该种花括号{}定义有效。作用:占行,而非输入元素,这和一维不一样*/
	//vector<vector<int>>c{ 1,2,3 };					/*无效定义*/
	vector<vector<int>>d{ N ,vector<int> {T} };			/*该种定义,支持行定义,不支持列定义,但支持行添加*/
	vector<vector<int>>d1{ N ,vector<int> {1,2,3} };	/*支持行直接输入元素,且是为每一行都插入*/
	vector<vector<int>>e;								/*正常定义,空的,无长度*/
}

3. vector定义方式建议

经过以上的一些探索,在这对不同vector定义方式的内存变化再做出一些分析和建议。

(1)一维vector

①一开始知道列长。

vector<int>a(10);

②一开始不知道列长,resize()策略。

vector<int>c;
...
c.resize(25);

优点:按需分配,有初始值,可以用下标直接改值。

③一开始不知道列长,reserve()+push_back()策略。

vector<int>a;
...
a.reserve(25);
for (i = 0; i < 25; i++)
{
	a.push_back(i + 1);
}

缺点:不建议用,虽然也是按需分配,但是由于push_back()可能带来内存溢出。

(2)二维vector(以类中定义为例)

①一开始知道行、列信息的,resize()策略。

vector<vector<int>>h(5);
for (i = 0; i < h.size(); i++)
{
	h[i].resize(10);
}

当对空vector采取resize(),capacity=size,但如果是对一个已有元素的vector进行resize(),或者是又对resize后的vector,push_back了一些元素,那么就会触发内存预留机制,所以如果要想去除多余的空间,可以采用如下方法:

vector<int>(h).swap(h)

优点:按需分配,有初始值,可以用下标直接改值。

②一开始知道行、列信息的,reserve()+push_back()策略。

不推荐!!!原因如下:

1)因为但凡用到push_back(),如果忘记清空,稍不注意,在某些循环中,循环一次就压入一堆元素,不光影响数据的使用,而且还会不停扩张内存,更严重的是当用完预留的内存后,更是成倍的开辟内存空间,直逼程序崩溃。

2)即便是记得使用清空的策略去除无关的元素,也容易错误的使用清空方式。比如:

vector<vector<int>> b{ N };
for (i = 0; i < N; i++)
{
	b[i].reserve(T);
}

该二维vector在定义时,已经指定了行数,所以“b.clear()”的行为是错误的,而“b[0],clear()”才是正确的,也即只能清空列。

又或者你想这样定义(错误!):

vector<vector<int>> e;
e.reserve(10);
for (i = 0; i < N; i++)
{
	b[i].reserve(10);
}

看着没问题,实际是个错误的定义。若一开始没有定义行,那么是无法通过reserve()固定行的;只能通过resize()的方式。

总结起来就是,这种策略的定义方式,特别容易出错!

③其他的情况暂且不论,总之能用resize()就用,能不用push_back()就不用,一旦用了,根据情况,需要清空的一定要正确及时的清空,避免内存损耗和溢出!

4. vector清空元素与内存释放

由于vector的内存占用空间只增不减,比如你首先分配了10,000个字节,然后erase掉后面9,999个,留下一个有效元素,但是内存占用仍为10,000个。所有内存空间是在vector析构时候才能被系统回收。empty()用来检测容器是否为空的,clear()可以清空所有元素。但是即使clear(),vector所占用的内存空间依然如故,无法保证内存的回收。

就像前面所说的,vector的内存空间是只增加不减少的,我们常用的操作clear()和erase(),实际上只是减少了size(),清除了数据,并不会减少capacity,所以内存空间没有减少。那么如何释放内存空间呢,正确的做法是swap()操作。

swap交换技巧实现内存释放思想:vector()使用vector的默认构造函数建立临时vector对象,再在该临时对象上调用swap成员,swap调用之后原来vector占用的空间就等于一个默认构造的对象的大小,临时对象就具有原来对象v的大小,而该临时对象随即就会被析构,从而其占用的空间也被释放。

当 swap() 成员方法用于清空 vector 容器时,可以套用如下的语法格式:

vector<T>().swap(x);

这里没有为 vector<T>() 表达式传递任何参数。这意味着,此表达式将调用 vector 模板类的默认构造函数,而不再是复制构造函数。也就是说,此格式会先生成一个空的 vector 容器,再借助 swap() 方法将空容器交换给 x,从而达到清空 x 的目的。

完整测试代码:

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

#define N 20
#define T 10

/*类Vector_define:探究在类中对vector进行定义的特点*/
class Vector_define
{
private:
	int i, j;
	/*一维vector*/
	//vector<int>a(N);				/*在自定义类中无法对vector实现圆括号()初始化长度的定义*/
	vector<int>a{ N };				/*此种花括号{}的初始化可以定义。作用:只是输入一个元素,而非定义长度*/
	vector<int>a1{ 1,2,3 };			/*支持直接输入元素*/
	vector<int>b;					/*正常定义,空的,无长度*/
	
	/*二维vector*/
	//vector<vector<int>>c(N, vector<int>(T));			/*该种定义同一维,无效定义*/
	vector<vector<int>>c{ N };							/*二维vector该种花括号{}定义有效。作用:占行,而非输入元素,这和一维不一样*/
	//vector<vector<int>>c{ 1,2,3 };					/*无效定义*/
	vector<vector<int>>d{ N ,vector<int> {T} };			/*该种定义,支持行定义,不支持列定义,但支持行添加*/
	vector<vector<int>>d1{ N ,vector<int> {1,2,3} };	/*支持行直接输入元素,且是为每一行都插入*/
	vector<vector<int>>e;								/*正常定义,空的,无长度*/

public:
	void test1()
	{
		/*teat a*/
		cout << "a.size()=" << a.size() << endl;	/* a.size()=1 ,可知类中花括号只是添加了元素而已*/
		for (i = 0; i < a.size(); i++)
		{
			cout << a[i] << "  ";					/* 只输出 20 ,也即只有一个元素*/
		}
		cout << endl << endl;
		
		/*teat b*/
		cout << "b.size()=" << b.size() << endl;	/* b.size()=0 ,没有元素,正常*/
		/*for (i = 0; i < a.size(); i++)
		{
			cout << b[i] << "  ";					// 报错:out of range! 空的容器,没有长度,没有东西输出!
		}
		cout << endl ;*/
		cout << endl;

		/*teat c*/
		cout << "c.size()=" << c.size() << endl;	/* c.size()=20,可知二维vector支持花括号{}定义行数 */
		for (i = 0; i < c.size(); i++)
		{
			for (j = 0; j < c[i].size(); j++)
			{
				cout << c[i][j] << "  ";			/* 不报错,有行没列,输出不了什么, */
			}
			cout << endl;
		}
		cout << endl;

		/*teat d*/
		cout << "d.size()=" << d.size() << endl;	/* d.size()=20,花括号{}行定义仍旧有效 */
		for (i = 0; i < d.size(); i++)
		{
			cout << "d[" << i << "].size()=" << d[i].size() << "  ";
		}											/* d[i].size()=1,可知花括号{}列长定义无效 */
		cout << endl;
		for (i = 0; i < d.size(); i++)
		{
			for (j = 0; j < d[i].size(); j++)
			{
				cout << d[i][j] << "  ";			/* d[i][j]=T,可知花括号{}列长定义无效,只是添加了一个元素 */
			}
			cout << endl;
		}
		cout << endl;

		/*teat e*/
		cout << "e.size()=" << e.size() << endl;	/* e.size()=0,行0,列空 */
		for (i = 0; i < e.size(); i++)
		{
			cout << "e[" << i << "].size()=" << e[i].size() << "  ";
		}											/* 列空 */
		cout << endl;
		for (i = 0; i < e.size(); i++)
		{
			for (j = 0; j < e[i].size(); j++)
			{
				cout << e[i][j] << "  ";			/* 容器空 */
			}
			cout << endl;
		}
		cout << endl;
	}
};
/*类Vector_memory:探究在类中定义的vector的内存特点*/
class Vector_memory
{
private:
	int i, j;
	/*一维*/
	vector<int>a;
	vector<int>b{ 1,2,3 };
	vector<int>c;
	vector<int>d;
	
	/*二维*/
	vector<vector<int>>e;
	vector<vector<int>>f{ N };
	vector<vector<int>>g{ N,vector<int>{1,2,3} };

public:
	void test2()
	{
		/*一维内存*/
		cout << "a.capacity()=" << a.capacity() << endl;	/* a.capacity()=0 */
		cout << "a.size()=" << a.size() << endl << endl;	/* a.capacity()=0 */
	
		cout << "b.capacity()=" << b.capacity() << endl;	/* a.capacity()=3,不会预留空间*/
		cout << "b.size()=" << b.size() << endl << endl;	/* a.capacity()=3 */
	
		//①往 a 中添加元素:研究 pushback() 对内存的改变
		for (i = 0; i < N; i++)
		{
			a.push_back(i + 1);
		}
		cout << "a.capacity()=" << a.capacity() << endl;	/* a.capacity()=28,可知pushback()会预留部分空间*/
		cout << "a.size()=" << a.size() << endl << endl;	/* a.size()=20,实际空间等于元素个数*/

		//②给 a 重设长度:研究 resize() 对内存的改变
		a.resize(30);
		cout << "a.capacity()=" << a.capacity() << endl;	/* a.capacity()=42,可知resize()会对缓存区进行重新分配和空间预留*/
		cout << "a.size()=" << a.size() << endl << endl;	/* a.capacity()=30,可知resize()会产生初始值*/
															
		//③给 c 重设长度:研究 resize() 对内存的改变
		c.resize(25);
		cout << "c.capacity()=" << c.capacity() << endl;	/* c.capacity()=25,可以发现:当对空容器resize(),不会预留空间*/
		cout << "c.size()=" << c.size() << endl << endl;	/* c.capacity()=25*/

		//④给 a 预设长度:研究 reserve() 对内存的改变
		a.reserve(50);
		cout << "a.capacity()=" << a.capacity() << endl;	/* a.capacity()=50,可知reserve()不会对缓存区进行重新分配和空间预留*/
		cout << "a.size()=" << a.size() << endl << endl;	/* a.size()=30,可知reserve()不会产生初始值*/
		
		//⑤给 a 添加超出它预设capcity的元素:研究 capcity 的变化
		for (i = 0; i < 25; i++)
		{
			a.push_back(i + 1);
		}
		cout << "a.capacity()=" << a.capacity() << endl;	/* a.capacity()=75,可知当元素超出预设的capcity,就会触发空间预留*/
		cout << "a.size()=" << a.size() << endl << endl;	/* a.size()=55*/
		
		/*二维内存*/
		cout << "e.capacity()=" << e.capacity() << endl;		/* e.capacity()=0,列的capcity报错 */
		
		cout << "f.capacity()=" << f.capacity() << endl;		/* f.capacity()=20 */
		cout << "f[0].capacity()=" << f[0].capacity() << endl;	/* f[0].capacity()=0,指定行则有列的capcity */

		cout << "g.capacity()=" << g.capacity() << endl;		/* g.capacity()=20 */
		cout << "g[2].capacity()=" << g[2].capacity() << endl;	/* g[2].capacity()=3,可知每行都被插入了相同的元素 */

		/*经过一维vector的探究,选出一些适合二维的节省资源的定义方式!*/

		//①对于可求得固定行、列信息的二维vector,定义如下:
		vector<vector<int>>h{ N };
		for (i = 0; i < h.size(); i++)
		{
			h[i].reserve(T);
		}
		cout << "h.capacity()=" << h.capacity() << endl;		/* h.capacity()=20 */
		cout << "h[0].capacity()=" << h[0].capacity() << endl;	/* h[0].capacity()=10 */
		cout << endl;

		//②对于可求得固定行信息,但是列长未知的二维vector,定义如下:
		vector<vector<int>>p{ N };
		/*通过push_back压入元素,会有预留空间*/
		for (i = 0; i < p.size(); i++)
		{
			for (j = 0; j < i + 1; j++)
			{
				p[i].push_back(j + 1);
			}
		}
		/*看看预留空间:发现确实有预留现象!*/
		for (i = 0; i < p.size(); i++)
		{
			cout << "p[" << i << "].size()=" << p[i].size() << endl;
			cout << "p[" << i << "].capacity()=" << p[i].capacity() << endl;
		}
		cout << endl;
		/*有没有办法切去多余的空间?——利用swap()方法去除vector多余的容量*/
		vector<vector<int>>(p).swap(p);
		for (i = 0; i < p.size(); i++)
		{
			cout << "p[" << i << "].size()=" << p[i].size() << endl;
			cout << "p[" << i << "].capacity()=" << p[i].capacity() << endl;
		}
		cout << endl;

		//③对于无任何信息的二维vector,直接定义空的就行。
		vector<vector<int>>t;
		/*注意:若一开始没有定义行,那么是无法通过reserve()固定行的;
			只能通过resize()的方式。
		*/
	}
};
/*类Vector_release:探究如何彻底释放在类中定义的vector的内存*/
class Vector_release
{
private:
	int i, j;
	
public:
	vector<vector<int>> a;
	vector<vector<int>> b{ N };
	vector<vector<int>> c;
	vector<int>d{ 1,2,3 };
	
	void define()
	{
		//①经过以上测试的探索,我们可以有新的定义方式:用 行、列resize()+swap()
		a.resize(N);
		for (i = 0; i < a.size(); i++)
		{
			a[i].resize(i + 1);
		}
		vector<vector<int>>(a).swap(a);			/*切除多余空间*/
		//输出
		for (i = 0; i < a.size(); i++)
		{
			for (j = 0; j < a[i].size(); j++)
			{
				cout << a[i][j] << " ";
			}
			cout << endl;
		}
		cout << endl;
		/*cout << "a.size()" << a.size() << endl;
		for (i = 0; i < a.size(); i++)
		{
			cout << "a[" << i << "].capacity()=" << a[i].capacity() << endl;
		}
		cout << endl;*/

		//②用 指定行、列reserve()+push_back()定义
		for (i = 0; i < N; i++)
		{
			b[i].reserve(T);
		}
		for (i = 0; i < N; i++)
		{
			for (j = 0; j < T; j++)
			{
				b[i].push_back(1);
			}
		}
		cout << "bbbbb" << endl;
		for (i = 0; i < b.size(); i++)
		{
			for (j = 0; j < b[i].size(); j++)
			{
				cout << b[i][j] << "  ";
			}
			cout << endl;
		}
		cout << endl;

		//③copy一个
		c = a;


		

	}

	void clean()
	{
		

		//b.clear();	/*报错!:因为b在定义时,指定了行数,所以不能clear行*/
		
		//for (i = 0; i < b.size(); i++)
		//{
		//	b[i].clear();	
		//}/*可以清空列元素,但是列的capacity还在。但至少可以保证循环时的push_back()不会溢出内存*/
		
		for (i = 0; i < b.size(); i++)
		{
			vector<int>().swap(b[i]);
		}/*可以清空列元素以及列的capacity。*/

		//c.clear();/*二维vector.clear(),可以彻底清除列的capacity,但是保留着行的capacity*/

		vector<vector<int>>().swap(c);/*利用swap()彻底清除capacity*/
		
		d.clear();/*一维vector.clear(),保留着列的capacity*/
		
	}

};
int main()
{
	Vector_define v1;
	v1.test1();

	Vector_memory v2;
	v2.test2();

	Vector_release v3;
	for (int it = 0; it < 4; it++)
	{
		cout << "第" << it << "次:" << endl;
		
		v3.define();
		v3.clean();
		
		for (int i = 0; i < v3.a.size(); i++)
		{
			cout << "a[i].capacity[" << i << "]=" << v3.a[i].capacity() << "  ";
		}
		cout << endl << endl;
		
		for (int i = 0; i < v3.b.size(); i++)
		{
			cout << "b[i].capacity[" << i << "]=" << v3.b[i].capacity() << "  ";
		}
		cout << endl << endl;
		
		cout << "c.capacity()=" << v3.c.capacity() << endl;
		for (int i = 0; i < v3.c.size(); i++)
		{
			cout << "c[i].capacity[" << i << "]=" << v3.c[i].capacity() << "  ";
		}
		cout << endl;
		cout << "d.capacity()=" << v3.d.capacity() << endl;
	}
	
	
	



	return 0;
	system("pause");
}

参考文章:

1.reserve和resize_lusic01的专栏-CSDN博客

2.C++ vector中的resize,reserve,size和capacity函数讲解

3.C++ vector swap()去除多余容量

4.vector内存机制和性能分析_https://github.com/JelinYao-CSDN博客_vector 内存

5.vector 的内存分配问题_藏经阁-CSDN博客

6.STL中vector的内存分配与正确释放_System Architect-CSDN博客_vector指针内存释放

7.c++ vector 初始化_C++语言之vector内存分配技术

8.vector的内存分配方式_zhuzhubility的博客-CSDN博客

https://zhuanlan.zhihu.com/p/338390842

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值