C++自定义list实现

概念

list是一个双向链表,可高效地进行插入删除元素。包括构造、方法等。

1. stl_list

模板参数

  • T

    • 类型:元素的类型。
    • 别名:成员类型 list::value_type
  • Alloc

    • 类型:用于定义存储分配模型的分配器对象的类型。默认情况下,使用分配器类模板,它定义了最简单的内存分配模型,与值类型无关。
    • 别名:成员类型 list::allocator_type

成员类型

  • value_type

    • 定义:第一个模板参数(T)。
  • allocator_type

    • 定义:第二个模板参数(Alloc)。
    • 默认值allocator<value_type>
  • reference

    • 定义allocator_type::reference
    • 对于默认的分配器value_type&
  • const_reference

    • 定义allocator_type::const_reference
    • 对于默认的分配器const value_type&
  • pointer

    • 定义allocator_type::pointer
    • 对于默认的分配器value_type*
  • const_pointer

    • 定义allocator_type::const_pointer
    • 对于默认的分配器const value_type*
  • iterator

    • 定义:到 value_type 的双向迭代器。
    • 可转换为const_iterator
  • const_iterator

    • 定义:到 const value_type 的双向迭代器。
  • reverse_iterator

    • 定义reverse_iterator<iterator>
  • const_reverse_iterator

    • 定义reverse_iterator<const_iterator>
  • difference_type

    • 定义:一个有符号整型,等同于 iterator_traits<iterator>::difference_type
    • 通常等同于ptrdiff_t
  • size_type

    • 定义:一个无符号整型,可以表示任何 difference_type 的非负值。
    • 通常等同于size_t

 成员函数

构造函数和析构函数

  • constructor:构造 list(公共成员函数)。
  • destructor:销毁 list(公共成员函数)。
  • operator=:赋值内容(公共成员函数)。

迭代器

  • begin:返回指向开头的迭代器(公共成员函数)。
  • end:返回指向末尾的迭代器(公共成员函数)。
  • rbegin:返回指向逆向开头的迭代器(公共成员函数)。
  • rend:返回指向逆向末尾的迭代器(公共成员函数)。
  • cbegin:返回指向开头的常量迭代器(公共成员函数)。
  • cend:返回指向末尾的常量迭代器(公共成员函数)。
  • crbegin:返回指向逆向开头的常量迭代器(公共成员函数)。
  • crend:返回指向逆向末尾的常量迭代器(公共成员函数)。

容量

  • empty:测试容器是否为空(公共成员函数)。
  • size:返回大小(公共成员函数)。
  • max_size:返回最大可容纳的大小(公共成员函数)。

元素访问

  • front:访问第一个元素(公共成员函数)。
  • back:访问最后一个元素(公共成员函数)。

修改器

  • assign:赋值新内容到容器(公共成员函数)。
  • emplace_front:在开头构造并插入元素(公共成员函数)。
  • push_front:在开头插入元素(公共成员函数)。
  • pop_front:删除第一个元素(公共成员函数)。
  • emplace_back:在末尾构造并插入元素(公共成员函数)。
  • push_back:在末尾插入元素(公共成员函数)。
  • pop_back:删除最后一个元素(公共成员函数)。
  • emplace:构造并插入元素(公共成员函数)。
  • insert:插入元素(公共成员函数)。
  • erase:擦除元素(公共成员函数)。
  • swap:交换内容(公共成员函数)。
  • resize:改变大小(公共成员函数)。
  • clear:清除内容(公共成员函数)。

操作

  • splice:从一个 list 转移元素到另一个 list(公共成员函数)。
  • remove:移除具有特定值的元素(公共成员函数)。
  • remove_if:移除符合条件的元素(公共成员函数模板)。
  • unique:移除重复值(公共成员函数)。
  • merge:合并有序列表(公共成员函数)。
  • sort:对容器内元素排序(公共成员函数)。
  • reverse:逆序容器内元素(公共成员函数)。

观察者

  • get_allocator:获取分配器(公共成员函数)。

以上是 std::list 类的常见成员函数,这些函数覆盖了创建、访问、修改和操作列表的所有基础功能。

2. list基本结构

1. 节点结构 (ListNode)

ListNode 结构体用于表示链表中的每个节点,它包含了数据、前驱指针和后继指针。

  • _data:存储节点的数据。
  • _prev:指向前一个节点。
  • _next:指向后一个节点。
  • 构造函数初始化数据并将前驱和后继指针设为 nullptr

2. 迭代器实现 (ListIterator)

ListIterator 类用于遍历链表,封装了节点指针并提供了一些操作符重载方法。

  • _node:指向当前迭代器所指向的节点。
  • 提供了 ==!= 运算符重载来比较迭代器。
  • 提供了解引用运算符 * 和成员访问运算符 ->
  • 前置和后置的 ++-- 运算符重载用于迭代器的移动。

3. 链表类 (list)

list 类封装了双向链表的操作,包括构造函数、析构函数、插入、删除等操作。 

3. 代码实现

3.1 重载list中运算符

iterator begin()
{
	//iterator it(_head->_next);
	//return it;
	return iterator(_head->_next);
}

const_iterator begin() const				// 只读
{
	return const_iterator(_head->_next);
}

iterator end()
{
	return iterator(_head);
}

const_iterator end() const					// 只读
{
	return const_iterator(_head);
}

3.2 迭代器的封装

因为自定义的类型++不能支持链表遍历,所以我们需要用一个外壳包住它。

3.2.1 自定义两个类

因为有const和非const之分,我们得有两个类  ListIterator 和 ListConstIterator

 ListIterator:

//迭代器实现(封装1)
template <class T>
class ListIterator
{
	typedef ListNode<T> Node;
	typedef ListIterator<T> self;

	Node* _node;
public:

	ListIterator(Node* node)		// 构造
		:_node(node)
	{}
			
	bool operator==(const self& it)	// == 运算符重载
	{
		return _node == it._node;
	}
	
	bool operator!=(const self& it)	// != 运算符重载
	{
		return _node != it._node;
	}

	T& operator*()					// 解引用 运算符重载
	{
		return _node->_data;
	}

	self& operator++()				// 前置++
	{
		_node = _node->_next;
		return *this;
	}

	self& operator--()				// 前置--
	{
		_node = _node->_prev;
		return *this;
	}

	self operator++(int)			// 后置++
	{
		self tmp(node);
		_node = _node->_next;

		return tmp;
	}

	self operator--(int)			// 后置--
	{
		self tmp(node);
		_node = _node->_prev;

		return tmp;
	}

	T* operator->()					// -> 运算符重载
	{
		return &_node->_data;
	}
};

 ListConstIterator:

//迭代器实现(封装2)
template <class T>
class ListConstIterator
{
	typedef ListNode<T> Node;
	typedef ListConstIterator<T> self;

	Node* _node;

public:
	ListConstIterator(Node* node)	// 构造: 
		:_node(node)
	{}
	
	bool operator==(const self& it)	// == 运算符重载
	{
		return _node == it._node;
	}

	bool operator!=(const self& it)	// != 运算符重载
	{
		return _node != it._node;
	}

	const T& operator*()			// 解引用 运算符重载
	{
		return _node->_data;
	}

	self& operator++()				// 前置++
	{
		_node = _node->_next;
		return *this;
	}

	self& operator--()				// 前置--
	{
		_node = _node->_prev;
		return *this;
	}

	self operator++(int)			// 后置++
	{
		self tmp(_node);
		_node = _node->_next;
		return tmp;
	}

	self operator--(int)			// 后置--
	{
		self tmp(_node);
		_node = _node->_prev;
		return tmp;
	}

	const T* operator->() const		// -> 运算符重载
	{
		return &_node->_data;
	}
};

这里提及一下:

void Func(const list<int>& lt2)
{
	list<int>::const_iterator it2 = lt2.begin();
	//这里注意:对于lt2需要有一处了解
	cout << "const_literator: ";
	while (it2 != lt2.end())
	{
		//*it2 += 10;//只可读
		cout << *it2 << " ";
		++it2;
	}
}

对于拥有常性的对象 lt2.begin() 可以调用非const的成员函数,如 operator++()

list<int>::const_iterator it2 = ++lt2.begin();

 这里我们可以很明显看出来,分开来写两个迭代器类造成了多处的代码冗余,为了使代码更简洁可以使用模板参数规定返回值类型,可以将两个迭代器类合并为一个。

但二者的本质是一样的

3.2.2 使用模板参数实例化两个类

//迭代器实现(通过模板,给不同参数实现两个类的实例化)
template <class T, class Ref, class Ptr>
class ListIterator
{

	typedef ListNode<T> Node;
	typedef ListIterator<T, Ref, Ptr> self;

	Node* _node;
public:

	ListIterator(Node* node)		// 构造
		:_node(node)
	{}

	bool operator==(const self& it)		// == 运算符重载
	{
		return _node == it._node;
	}

	bool operator!=(const self& it)		// != 运算符重载
	{
		return _node != it._node;
	}

	Ref operator*()						// 解引用 运算符重载
	{
		return _node->_data;
	}

	self& operator++()					// 前置++
	{
		_node = _node->_next;
		return *this;
	}

	self& operator--()					// 前置--
	{
		_node = _node->_prev;
		return *this;
	}

	self operator++(int)				// 后置++
	{
		self tmp(*this);
		_node = _node->_next;

		return tmp;
	}

	self operator--(int)				// 后置--
	{
		self tmp(*this);
		_node = _node->_prev;

		return tmp;
	}

	Ptr operator->()					// -> 运算符重载
	{
		return &_node->_data;
	}
};

3.2.3 最终list中实例化迭代器

template <class T>
class list
{
	typedef ListNode<T> Node;					// 结点

public:

//使用自定义类的方式:
	//typedef ListIterator<T> iterator;			
	//typedef ListConstIterator<T> const_iterator;

//使用模版参数:
	typedef ListIterator<T, T&, T*> iterator;
	typedef ListIterator<T, const T&, const T*> const_iterator;

	iterator begin()
	{
		//iterator it(_head->_next);
		//return it;
		return iterator(_head->_next);
	}

	const_iterator begin() const				// 只读
	{
		return const_iterator(_head->_next);
	}

	iterator end()
	{
		return iterator(_head);
	}

	const_iterator end() const					// 只读
	{
		return const_iterator(_head);
	}
												
	//...
};

3.3 操作函数

void push_back(const T& x)					// 尾插
{
	Node* newnode = new Node(x);
	Node* tail = _head->_prev;				// 尾指针指向哨兵节点的前一个结点

	tail->_next = newnode;
	newnode->_prev = tail;
	newnode->_next = _head;
	_head->_prev = newnode;
}
// 不涉及扩容,insert中没有迭代器失效问题
void insert(iterator pos, const T& x)
{
	Node* cur = pos._node;
	Node* newnode = new Node(x);
	Node* prev = cur->_prev;
	// prev  newnode  cur
	prev->_next = newnode;
	newnode->_prev = prev;
	newnode->_next = cur;
	cur->_prev = newnode;
}
// erase 后 pos失效,pos指向的结点被释放
iterator erase(iterator pos)
{
	assert(pos != end());// 防止哨兵节点被删除

	Node* cur = pos._node;
	Node* prev = cur->_prev;
	Node* next = cur->_next;

	prev->_next = next;
	next->_prev = prev;

	delete cur;
	return iterator(next);
}
// 尾删
void pop_back()
{
	erase(--end());
}
// 头插
void push_front(const T& x)
{
	insert(begin(), x);
}
// 头删
void pop_front()
{
	erase(begin());
}

在C++中,私有成员只能被声明它们的类和它们的友元访问。之前的迭代器成员变量 _node  private 

我们有两种解决方案,一是将ListIterator类中的_node设为共有,二是将class 改成结构体

因为方法和变量都是共有,我们不如使用struct,,同样STL库中也是用的struct

4. 最终代码:

#pragma once

#include <iostream>
#include <assert.h>

using namespace std;

template <class T>
struct ListNode
{ // 结点结构
	T _data;
	ListNode* _prev;
	ListNode* _next;

	ListNode(const T& data = T()) // 结点默认参数
		: _data(data), _prev(nullptr), _next(nullptr) {}
};

namespace bit 
{
	// list 结点结构
	

	/*
	//迭代器实现(封装1)
	template <class T>
	class ListIterator
	{
		typedef ListNode<T> Node;
		typedef ListIterator<T> self;

		Node* _node;
	public:

		ListIterator(Node* node)		// 构造
			:_node(node)
		{}
				
		bool operator==(const self& it)	// == 运算符重载
		{
			return _node == it._node;
		}
		
		bool operator!=(const self& it)	// != 运算符重载
		{
			return _node != it._node;
		}

		T& operator*()					// 解引用 运算符重载
		{
			return _node->_data;
		}

		self& operator++()				// 前置++
		{
			_node = _node->_next;
			return *this;
		}

		self& operator--()				// 前置--
		{
			_node = _node->_prev;
			return *this;
		}

		self operator++(int)			// 后置++
		{
			self tmp(_node);
			_node = _node->_next;

			return tmp;
		}

		self operator--(int)			// 后置--
		{
			self tmp(_node);
			_node = _node->_prev;

			return tmp;
		}

		T* operator->()					// -> 运算符重载
		{
			return &_node->_data;
		}
	};

	//迭代器实现(封装2)
	template <class T>
	class ListConstIterator
	{
		typedef ListNode<T> Node;
		typedef ListConstIterator<T> self;

		Node* _node;

	public:
		ListConstIterator(Node* node)	// 构造: 
			:_node(node)
		{}
		
		bool operator==(const self& it)	// == 运算符重载
		{
			return _node == it._node;
		}

		bool operator!=(const self& it)	// != 运算符重载
		{
			return _node != it._node;
		}

		const T& operator*()			// 解引用 运算符重载
		{
			return _node->_data;
		}

		self& operator++()				// 前置++
		{
			_node = _node->_next;
			return *this;
		}

		self& operator--()				// 前置--
		{
			_node = _node->_prev;
			return *this;
		}

		self operator++(int)			// 后置++
		{
			self tmp(_node);
			_node = _node->_next;
			return tmp;
		}

		self operator--(int)			// 后置--
		{
			self tmp(_node);
			_node = _node->_prev;
			return tmp;
		}

		const T* operator->() const		// -> 运算符重载
		{
			return &_node->_data;
		}
	};
	*/
	
	//迭代器实现(通过模板,给不同参数实现两个类的实例化)
	template <class T, class Ref, class Ptr>
	struct ListIterator
	{

		typedef ListNode<T> Node;
		typedef ListIterator<T, Ref, Ptr> self;

		Node* _node;

		ListIterator(Node* node)		// 构造
			:_node(node)
		{}

		bool operator==(const self& it)		// == 运算符重载
		{
			return _node == it._node;
		}

		bool operator!=(const self& it)		// != 运算符重载
		{
			return _node != it._node;
		}

		Ref operator*()						// 解引用 运算符重载
		{
			return _node->_data;
		}

		self& operator++()					// 前置++
		{
			_node = _node->_next;
			return *this;
		}

		self& operator--()					// 前置--
		{
			_node = _node->_prev;
			return *this;
		}

		self operator++(int)				// 后置++
		{
			self tmp(*this);
			_node = _node->_next;

			return tmp;
		}

		self operator--(int)				// 后置--
		{
			self tmp(*this);
			_node = _node->_prev;

			return tmp;
		}

		Ptr operator->()					// -> 运算符重载
		{
			return &_node->_data;
		}
	};

	template <class T>
	class list
	{
		typedef ListNode<T> Node;					// 结点

	public:

		//使用自定义类的方式:
			//typedef ListIterator<T> iterator;			
			//typedef ListConstIterator<T> const_iterator;

		//使用模版参数:
		typedef ListIterator<T, T&, T*> iterator;
		typedef ListIterator<T, const T&, const T*> const_iterator;

		iterator begin()
		{
			//iterator it(_head->_next);
			//return it;
			return iterator(_head->_next);
		}

		const_iterator begin() const				// 只读
		{
			return const_iterator(_head->_next);
		}

		iterator end()
		{
			return iterator(_head);
		}

		const_iterator end() const					// 只读
		{
			return const_iterator(_head);
		}

		//头结点初始化 默认构造和拷贝构造都需要,单独提出来,复用减少冗余
		void empty_init()
		{
			_head = new Node();
			_head->_next = _head;
			_head->_prev = _head;
		}

		list()										// 默认构造
		{    
			empty_init();
		}

		list(const list<T>& lt)						// 默认构造
		{
			empty_init();
			// 遍历
			for (const auto& e : lt)
			{
				push_back(e);
			}
		}
		//初始化列表
		list(initializer_list<T> il)				//不需要通过引用传递,因为它已经高效地包装了一个指向常量数组的指针。
		{
			empty_init();
			for (const auto& e : il)
			{
				push_back(e);
			}
		}
		//赋值拷贝
		list<T>& operator=(list<T> lt)
		{
			swap(_head, lt._head);

			return *this;
		}
		~list()										// 析构函数
		{
			clear();
			delete _head;
			_head = nullptr;
		}
		
		void clear()	// 这里注意不能把头结点删除,后面还要复用
		{
			Node* curr = _head->_next;
			while (curr != _head)
			{
				Node* next = curr->_next;
				delete curr;
				curr = next;
			}
			
			/*
			//也可以复用写过的函数
			Node* cur = begin();
			while (cur != end())
			{

				delete cur;
				next = cur->_next;
			}
			*/
		}
		//尾插
		void push_back(const T& x)					// 尾插
		{
			Node* newnode = new Node(x);
			Node* tail = _head->_prev;				// 尾指针指向哨兵节点的前一个结点

			tail->_next = newnode;
			newnode->_prev = tail;
			newnode->_next = _head;
			_head->_prev = newnode;
		}
		// 不涉及扩容,insert中没有迭代器失效问题
		void insert(iterator pos, const T& x)
		{
			Node* cur = pos._node;
			Node* newnode = new Node(x);
			Node* prev = cur->_prev;
			// prev  newnode  cur
			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = cur;
			cur->_prev = newnode;
		}
		// erase 后 pos失效,pos指向的结点被释放
		iterator erase(iterator pos)
		{
			assert(pos != end());// 防止哨兵节点被删除

			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* next = cur->_next;

			prev->_next = next;
			next->_prev = prev;

			delete cur;
			return iterator(next);
		}
		// 尾删
		void pop_back()
		{
			erase(--end());
		}
		// 头插
		void push_front(const T& x)
		{
			insert(begin(), x);
		}
		// 头删
		void pop_front()
		{
			erase(begin());
		}
		
		// 访问第一个元素
		T& front() {
			return *begin();
		}
		// 只读
		const T& front() const {
			return *begin();
		}
		// 访问最后一个元素
		T& back() {
			return *(--end());
		}
		// 只读
		const T& back() const {
			return *(--end());
		}
		// 判空
		bool empty() const {
			return begin() == end();
		}
		// 返回链表的元素数量
		size_t size() const {
			size_t size = 0;
			for (auto it = begin(); it != end(); ++it) {
				++size;
			}
			return size;
		}
		// 交换两个链表的内容
		void swap(list<T>& other) {
			std::swap(_head, other._head);
		}
		// 调整链表的大小
		void resize(size_t new_size, const T& value = T()) {
			size_t current_size = size();
			if (new_size < current_size) {
				while (current_size > new_size) {
					pop_back();
					--current_size;
				}
			}
			else if (new_size > current_size) {
				while (current_size < new_size) {
					push_back(value);
					++current_size;
				}
			}
		}
		// 用特定值填充链表
		void assign(size_t n, const T& value) {
			clear();
			for (size_t i = 0; i < n; ++i) {
				push_back(value);
			}
		}

	private:
		Node* _head;
	};
}

测试:

//测试环节:

	void Func(const list<int>& lt2)
	{
		list<int>::const_iterator it2 = lt2.begin();
		//这里注意:对于lt2需要有一处了解
		cout << "const_literator: ";
		while (it2 != lt2.end())
		{
			//*it2 += 10;//只可读
			cout << *it2 << " ";
			++it2;
		}
	}
	//迭代器的测试
	void test1()
	{
		list<int> lt1;

		lt1.push_back(1);
		lt1.push_back(2);
		lt1.push_back(3);
		lt1.push_back(4);
		lt1.push_back(5);

		list<int>::iterator it1 = lt1.begin();
		cout << "literator: ";
		while (it1 != lt1.end())
		{
			*it1 += 10;

			cout << *it1 << " ";
			++it1;
		}

		cout << endl;

		// 支持迭代器就支持了范围for
		for (auto e : lt1)
		{
			cout << e << " ";
		}

		cout << endl;
		
		Func(lt1);
		cout << endl;
	}

	//自定义坐标类型
	struct pos			
	{
		int _row;
		int _col;

		pos(int row = 0, int col = 0)	//list中的头结点需要默认构造,因此给缺省值
			:_row(row)
			,_col(col)
		{}
	};

	//两种方式访问成员
	void test2()
	{
		list<pos> lt;
		lt.push_back(pos(100, 100));
		lt.push_back(pos(200, 200));
		lt.push_back(pos(300, 300));
		
		list<pos>::iterator it1 = lt.begin();
		while (it1 != lt.end())
		{
			cout << (*it1)._row << "," << (*it1)._col << " ";
			++it1;
		}
		cout << endl;

		list<pos>::iterator it2 = lt.begin();
		while (it2 != lt.end())
		{	
			//为了可读性,省略了一个 '->'
			//可以写成:
			//cout << it2.operator->()->_row << "," << it2.operator->()->_col << " ";
			cout << it2->_row << "," << it2->_col << " ";
			++it2;
		}
		cout << endl;	
	}
	// 插入删除的测试
	void test3()
	{
		list<int> lt;
		lt.push_back(5);
		lt.push_back(6);
		lt.push_back(7);
		lt.push_back(8);
		lt.push_back(9);

		for (auto e : lt) { cout << e << " "; }
		cout << endl;

		lt.push_front(5);
		lt.push_front(4);
		lt.push_front(3);
		lt.push_front(2);
		lt.push_front(1);

		for (auto e : lt) { cout << e << " "; }
		cout << endl;

		lt.insert(++lt.begin(), 100);

		for (auto e : lt) { cout << e << " "; }
		cout << endl;

		lt.erase(--lt.end());

		for (auto e : lt) { cout << e << " "; }
		cout << endl;
	}
	// 深拷贝测试
	void test4()
	{
		list<int> lt1;

		lt1.push_back(1);
		lt1.push_back(2);
		lt1.push_back(3);

		for (auto e : lt1) { cout << e << " "; }
		cout << endl;

		lt1.push_back(4);		//这里如果是浅拷贝,lt2仍然能打印出4
		list<int> lt2(lt1);
		for (auto e : lt2) { cout << e << " "; }
		cout << endl;

	}
	// 简单接口的调用
	void test5()
	{
		list<int> lt1;
		list<int> lt2;

		lt1.push_back(1);
		lt1.push_back(2);
		lt1.push_back(3);
		cout << lt1.size() << endl;

		cout << lt1.front() << endl;
		cout << lt1.back() << endl;

		lt2.push_back(4);
		lt2.push_back(5);
		lt2.push_back(6);

		lt1.swap(lt2);

		for (auto e : lt1) { cout << e << " "; }
		cout << endl;

		for (auto e : lt2) { cout << e << " "; }
		cout << endl;

		lt2.assign(10, 100);
		
		for (auto e : lt2) { cout << e << " "; }
		cout << endl;
	}

int main()
{	
	cout << "test1():" << "\n";
	test1();

	cout << "test2():" << "\n";
	test2();

	cout << "test3():" << "\n";
	test3();
	
	cout << "test4():" << "\n";
	test4();

	cout << "test5():" << "\n";
	test5();

	return 0;
}

输出:

test1():
literator: 11 12 13 14 15
11 12 13 14 15
const_literator: 11 12 13 14 15
test2():
100,100 200,200 300,300
100,100 200,200 300,300
test3():
5 6 7 8 9
1 2 3 4 5 5 6 7 8 9
1 100 2 3 4 5 5 6 7 8 9
1 100 2 3 4 5 5 6 7 8
test4():
1 2 3
1 2 3 4
test5():
3
1
3
4 5 6
1 2 3
100 100 100 100 100 100 100 100 100 100

至此list中常见的接口函数我们实现完毕了,我们下期再见

  • 16
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值