c++STL知识总结

私人定制博客搭建全教程

C++ 的标准模板库(Standard Template Library,STL)是泛型程序设计最成功应用的实例。STL 是一些常用数据结构(如链表、可变长数组、排序二叉树)和算法(如排序、查找)的模板的集合,主要由 Alex Stepanov 主持开发,于 1998 年被加入 C++ 标准。

c++容器

容器(container)用于存放数据的类模板。可变长数组、链表、平衡二叉树等数据结构在 STL 中都被实现为容器.

顺序容器

元素在容器中的位置同元素的值无关,即容器不是排序的。将元素插入容器时,指定在什么位置(尾部、头部或中间某处)插入,元素就会位于什么位置。

主要包括:可变长动态数组vector,双端队列 deque、双向链表 list。

关联容器

关联容器内的元素是排序的。插入元素时,容器会按一定的排序规则将元素放到适当的位置上,因此插入元素时不能指定位置。

主要包括:set、multiset、map、multimap。

容器适配器

STL在两类容器的基础上屏蔽一部分功能,突出或增加另一部分功能,实现了三种容器适配器:栈(stack)队列(queue)、优先级队列 (priority_queue)。

通用操作

容器函数

所有容器都可以使用的成员函数

  • int size():返回容器对象中元素的个数。
  • bool empty():判断容器对象是否为空。

顺序容器和关联容器共有成员函数

  • begin():返回指向容器中第一个元素的迭代器。
  • end():返回指向容器中最后一个元素后面的位置的迭代器。
  • rbegin():返回指向容器中最后一个元素的反向迭代器。
  • rend():返回指向容器中第一个元素前面的位置的反向迭代器。
  • erase(…):从容器中删除一个或几个元素。
  • clear():从容器中删除所有元素。

顺序容器专享成员函数

  • front():返回容器中第一个元素的引用。
  • back():返回容器中最后一个元素的引用。
  • push_back():在容器末尾增加新元素。
  • pop_back():删除容器末尾的元素。
  • insert(…):插入一个或多个元素。

迭代器(Iterator)

迭代器是一个变量,相当于容器和操纵容器的算法之间的中介。迭代器可以指向容器中的某个元素,通过迭代器就可以读写它指向的元素。从这一点上看,迭代器和指针类似。

迭代器定义方式

  1. 正向迭代器

容器类名::iterator 迭代器名;
example:vector::iteartor i;

  1. 常量正向迭代器

容器类名::const_iterator 迭代器名;

  1. 反向迭代器

容器类名::reverse_iterator 迭代器名;

  1. 常量反向迭代器

容器类名::const_reverse_iterator 迭代器名;

###迭代器用法示例

迭代器都可以进行++操作

对正向迭代器进行++操作时,迭代器会指向容器中的后一个元素。
而对反向迭代器进行++操作时,迭代器会指向容器中的前一个元素。

#include <iostream>
#include <vector>
using namespace std;
int main()
{
    vector<int> v;  //v是存放int类型变量的可变长数组,初始为空
    for (int n = 0; n<5; ++n)
        v.push_back(n);  //push_back成员函数在vector容器尾部添加一个元素
    vector<int>::iterator i;  //定义正向迭代器
    for (i = v.begin(); i != v.end(); ++i) {  //用迭代器遍历容器
        cout << *i << " ";  //*i 就是迭代器i指向的元素
        *i += 2;  //每个元素变为原来的2倍
    }
    cout << endl;
    //用反向迭代器遍历容器
    for (vector<int>::reverse_iterator j = v.rbegin(); j != v.rend(); ++j)
        cout << *j << " ";
    system("pause");
    return 0;
}

运行结果:
0 1 2 3 4
6 5 4 3 2》

  1. 注意定义为常量迭代器时第12行’*i+=2;'是会报错的,迭代器的值不能更改。
  2. 容器适配器 stack、queue 和 priority_queue 没有迭代器。

迭代器功能分类

不同容器的迭代器,其功能强度是不同的
4. 正向迭代器
迭代器p是正向迭代器,则支持++p,p++,*p,==,!=
5. 双向迭代器(list,set/multiset,map/multimap)
包含正向迭代器的所有功能,还支持–p,p–
6. 随机访问迭代器(vector,deque)
包含前者功能,还支持随机访问
* p+=i:使得 p 往后移动 i 个元素。
* p-=i:使得 p 往前移动 i 个元素。
* p+i:返回 p 后面第 i 个元素的迭代器。
* p-i:返回 p 前面第 i 个元素的迭代器。
* p[i]:返回 p 后面第 i 个元素的引用。

  1. 两个随机访问迭代器 p1、p2 还可以用 <、>、<=、>= 运算符进行比较。p1<p2的含义是:p1 经过若干次(至少一次)++操作后,就会等于 p2。其他比较方式的含义与此类似。
  2. 对于两个随机访问迭代器 p1、p2,表达式p2-p1也是有定义的,其返回值是 p2 所指向元素和 p1 所指向元素的序号之差(也可以说是 p2 和 p1 之间的元素个数减一

示例代码

#include <iostream>
#include <vector>
using namespace std;
int main()
{
    vector<int> v(100); //v被初始化成有100个元素
    for(int i = 0;i < v.size() ; ++i) //size返回元素个数
        cout << v[i]; //像普通数组一样使用vector容器
    vector<int>::iterator i;
    for(i = v.begin(); i != v.end (); ++i) //用 != 比较两个迭代器
        cout << * i;
    for(i = v.begin(); i < v.end ();++i) //用 < 比较两个迭代器
        cout << * i;
    i = v.begin();
    while(i < v.end()) { //间隔一个输出
        cout << * i;
        i += 2; // 随机访问迭代器支持 "+= 整数"  的操作
    }
}

迭代器辅助函数

  1. advance(p, n):使迭代器 p 向前或向后移动 n 个元素。
  2. distance(p, q):计算两个迭代器之间的距离,即迭代器 p 经过多少次 + + 操作后和迭代器 q 相等。如果调用时 p 已经指向 q 的后面,则这个函数会陷入死循环。
  3. iter_swap(p, q):用于交换两个迭代器 p、q 指向的值。
    使用时需要引入algorithm库
#include <list>
#include <iostream>
#include <algorithm> //要使用操作迭代器的函数模板,需要包含此文件
using namespace std;
int main()
{
    int a[5] = { 1, 2, 3, 4, 5 };
    list <int> lst(a, a+5);
    list <int>::iterator p = lst.begin();
    advance(p, 1);  //p向后移动1个元素,指向2
    cout << "1)" << *p << endl;  //输出 1)2
    advance(p, -1);  //p向前移动一个元素,指向1
    cout << "2)" << *p << endl;  //输出 2)1
    list<int>::iterator q = lst.end();
    q--;  //q 指向 5
    cout << "3)" << distance(p, q) << endl;  //输出 3)4
    iter_swap(p, q); //交换 1 和 5
    cout << "4)";
    for (p = lst.begin(); p != lst.end(); ++p)
        cout << *p << " ";
    return 0;
}

STL算法:

  1. copy:将一个容器的内容复制到另一个容器。
  2. remove:在容器中删除一个元素。
  3. random_shuffle:随机打乱容器中的元素。
  4. fill:用某个值填充容器。
  5. find:在容器中查找元素。
  6. count_if:统计容器中符合某种条件的元素的个数。
  7. sort:排序

vector

顺序容器,支持STL中的所有算法,使用vector,需要包含vector头文件
随机访问元素的开销是常数,尾部添加元素的消耗时间也是常数,中间插入和删除涉及到元素的移动,消耗时间与元素个数有关
vector采用动态分配数组来存储元素,默认大小是32个元素存储空间,超过这个数时,需要把之前的元素复制到另外开辟的空间上,此时添加元素的时间开销不是常数。

成员函数解释
vector()无参构造函数,将容器初始化为空
vector(int n)将容器初始化为有 n 个元素
vector(int n, const T & val)假定元素的类型是 T,此构造函数将容器初始化为有 n 个元素,每 个元素的值都是 val
vector(iterator first, iterator last)first 和 last 可以是其他容器的迭代器。一般来说,本构造函数初始化的结果就是将 vector 容器的内容变成与其他容器上的区间 [first, last) —致
void clear()删除所有元素
bool empty()判断容器是否为空
void pop_back()删除容器末尾的元素
void push_back( const T & val)将 val 添加到容器末尾
int size()返回容器中元素的个数
T & front()返回容器中第一个元素的引用
T & back()返回容器中最后一个元素的引用
iterator insert(iterator i, const T & val)将 val 插入迭代器 i 指向的位置,返回 i
iterator insert( iterator i, iterator first, iterator last)将其他容器上的区间 [first, last) 中的元素插入迭代器 i 指向的位置
iterator erase(iterator i)删除迭代器 i 指向的元素,返回值是被删元素后面的元素的迭代器
iterator erase(iterator first, iterator last)删除容器中的区间 [first, last)
void swap( vector & v)将容器自身的内容和另一个同类型的容器 v 互换

list

顺序容器,双向链表
使用时需要引入list库文件
定位到元素位置之后,增删元素时间为常数

成员函数解释
void push_front(const T & val)将 val 插入链表最前面
void pop_front()删除链表最前面的元素
void sort()将链表从小到大排序
void remove (const T & val)删除和 val 相等的元素
remove_if删除符合某种条件的元素
void unique()删除所有和前一个元素相等的元素
void merge(list & x)将链表 x 合并进来并清空 x。要求链表自身和 x 都是有序的
void splice(iterator i, list & x, iterator first, iterator last)在位置 i 前面插入链表 x 中的区间 [first, last),并在链表 x 中删除该区间。链表自身和链表 x 可以是同一个链表,只要 i 不在 [first, last) 中即可

STL 中的算法 sort 可以用来对 vector 和 deque 排序,它需要随机访问迭代器的支持。因为 list 不支持随机访问迭代器,所以不能用算法 sort 对 list 容器排序。因此,list 容器引入了 sort 成员函数以完成排序。

#include <list>  //使用 list 需要包含此头文件
#include <iostream>
#include <algorithm>  //使用STL中的算法需要包含此头文件
using namespace std;
class A {
private: int n;
public:
    A(int n_) { n = n_; }
    friend bool operator < (const A & a1, const A & a2);
    friend bool operator == (const A & a1, const A & a2);
    friend ostream & operator << (ostream & o, const A & a);
};
bool operator < (const A & a1, const A & a2) {
    return a1.n < a2.n;
}
bool operator == (const A & a1, const A & a2) {
    return a1.n == a2.n;
}
ostream & operator << (ostream & o, const A & a) {
    o << a.n;
    return o;
}
template <class T>
void Print(T first, T last)
{
    for (; first != last; ++first)
        cout << *first << " ";
    cout << endl;
}
int main()
{
    A a[5] = { 1, 3, 2, 4, 2 };
    A b[7] = { 10, 30, 20, 30, 30, 40, 40 };
    list<A> lst1(a, a + 5), lst2(b, b + 7);
    lst1.sort();
    cout << "1)"; Print(lst1.begin(), lst1.end());  //输出:1)1 2 2 3 4
    lst1.remove(2);  //删除所有和A(2)相等的元素
    cout << "2)"; Print(lst1.begin(), lst1.end());  //输出:2)1 3 4
    lst2.pop_front();  //删除第一个元素
    cout << "3)"; Print(lst2.begin(), lst2.end());  //输出:3)30 20 30 30 40 40
    lst2.unique();  //删除所有和前一个元素相等的元素
    cout << "4)"; Print(lst2.begin(), lst2.end());  //输出:4)30 20 30 40
    lst2.sort();
    lst1.merge(lst2);  //合并 lst2 到 lst1 并清空 lst2
    cout << "5)"; Print(lst1.begin(), lst1.end());  //输出:5)1 3 4 20 30 30 40
    cout << "6)"; Print(lst2.begin(), lst2.end());  //lst2是空的,输出:6)
    lst1.reverse();  //将 lst1 前后颠倒
    cout << "7)"; Print(lst1.begin(), lst1.end());  //输出 7)40 30 30 20 4 3 1
    lst2.insert(lst2.begin(), a + 1, a + 4);  //在 lst2 中插入 3,2,4 三个元素
    list <A>::iterator p1, p2, p3;
    p1 = find(lst1.begin(), lst1.end(), 30);
    p2 = find(lst2.begin(), lst2.end(), 2);
    p3 = find(lst2.begin(), lst2.end(), 4);
    lst1.splice(p1, lst2, p2, p3);  //将[p2, p3)插入p1之前,并从 lst2 中删除[p2,p3)
    cout << "8)"; Print(lst1.begin(), lst1.end());  //输出:8)40 2 30 30 20 4 3 1
    cout << "9)"; Print(lst2.begin(), lst2.end());  //输出:9)3 4
    return 0;
}

deque

顺序容器,可变长数组,所以vector的成员函数,deque都有
deque 在头尾增删元素都具有较好的性能

相比于vector多了下面两个成员函数

void push_front (const T & val); //将 val 插入容器的头部
void pop_front(); //删除容器头部的元素

关联容器

关联容器内部的元素都是排好序的,有以下四种:
set:排好序的集合,不允许有相同元素。
multiset:排好序的集合,允许有相同元素。
map:每个元素都分为关键字和值两部分,容器中的元素是按关键字排序的。不允许有多个元素的关键字相同。
multimap:和 map 类似,差别在于元素的关键字可以相同。

学前了解:pair模板
{% note success %}
由于关联容器的一些成员函数的返回值是 pair 对象,而且 map 和 multimap 容器中的元素都是 pair 对象,所以在学习之前,首先认识一下pair对象
{% endnote %}
pair对象模板简介

set

set 是关联容器的一种,是排序好的集合(元素已经进行了排序)。set 中不能有重复的元素。

不能直接修改set容器中元素的值。因为元素被修改后,容器并不会自动重新调整顺序,于是容器的有序性就会被破坏,再在其上进行查找等操作就会得到错误的结果。因此,如果要修改set容器中某个元素的值,正确的做法是先删除该元素,再插入新元素。

成员函数解释
iterator find (const T & val);在容器中查找值为 val 的元素,返回其迭代器。如果找不到,返 回 end()
pair<iterator, bool> insert(const T & val);将 val 插入容器中并返回其迭代器和bool对象bool为true表示插入成功
void insert(iterator first, iterator last);将区间 [first, last) 中的元素插人容器
int count( const T & val);统计有多少个元素的值和 val 相等
iterator lower_bound( const T & val);查找一个最大的位置 it,使得 [begin(), it) 中所有的元素者比 val 小
iterator upper_bound( const T & val);查找一个最小的位置 it,使得 [it, end()) 中所有的元素都比 val 大
pair <iterator, iterator > equal_range (const T & val);同时求得 lower_bound 和 upper_bound
iterator erase(iterator it);删除 it 指向的元素,返回其后面的元素的迭代器(Visual Studio 2010 中如此,但是在 C++ 标准和 Dev C++ 中,返回值不是这样)
iterator erase(iterator first, iterator last);删除区间 [first, last),返回 last(Visual Studio 2010 中如此,但是在 C++ 标准和 Dev C++ 中,返回值不是这样)

使用小测试

#include <iostream>
#include <set>  //使用set须包含此文件
using namespace std;
int main()
{
    typedef set<int>::iterator IT;
    int a[5] = { 3,4,6,1,2 };
    set<int> st(a,a+5);    // st里是 1 2 3 4 6
    pair< IT,bool> result;
    result = st.insert(5); // st变成  1 2 3 4 5 6
    if(result.second)    //插入成功则输出被插入元素
        cout << * result.first  << " inserted" << endl; //输出: 5 inserted
    if(st.insert(5).second)
        cout << * result.first  << endl;
    else
        cout << * result.first << " already exists" << endl;
    //输出 5 already exists
    pair<IT,IT> bounds = st.equal_range(4);
    cout << * bounds.first << "," << * bounds.second ;  //输出:4,5
    return 0;
}

multiset

multiset 是关联容器的一种,是排序好的集合(元素已经进行了排序),并且允许有相同的元素。它和 et的差别在于set中不能有重复的元素。 set的成员函数 multiset 中都有。

map

map 是关联容器的一种,map 的每个元素都分为关键字和值两部分,容器中的元素是按关键字排序的,并且不允许有多个元素的关键字相同。
{% note warning %}
不能直接修改 map 容器中的关键字。因为 map 中的元素是按照关键字排序的,当关键字被修改后,容器并不会自动重新调整顺序,于是容器的有序性就会被破坏,再在其上进行查找等操作就会得到错误的结果。
{% endnote %}

成员函数和set大同小异,使用方法也类似

成员函数解释
iterator find( const Key & val);在容器中查找关键字等于 val 的元素,返回其迭代器;如果找不到,返回 end()
iterator insert (pair <Key, T> const &p);将 pair 对象 p 插入容器中并返回其迭代器
void insert(iterator first, iterator last);将区间 [first, last) 插入容器
int count( const Key & val);统计有多少个元素的关键字和 val 相等
iterator lower_bound( const Key & val);查找一个最大的位置 it,使得 [begin( ), it) 中所有的元素的关键字都比 val 小
iterator upper_bound(const Key & val);查找一个最小的位置 it,使得 [it, end()) 中所有的元素的关键字都比 val 大
pair < iterator, iterator > equal_range (const Key & val);同时求得 lower_bound 和 upper_bound
iterator erase(iterator it);删除 it 指向的元素,返回其后面的元素的迭代器(Visual Studio 2010 中如此,但是在 C++ 标准和 Dev C++ 中,返回值不是这样)
iterator erase(iterator first, iterator last);删除区间 [first, last),返回 last(Visual Studio 2010 中如此,但是在 C++ 标准和 Dev C++ 中,返回值不是这样)

multimap

与map操作基本相同,主要区别时key值允许重复

容器适配器

STL 中的容器适配器有 stack、queue、priority_queue 三种。它们都是在顺序容器的基础上实现的,屏蔽了顺序容器的一部分功能,突出或增加了另外一些功能。

容器适配器都有以下三个成员函数:

  • push:添加一个元素。
  • top:返回顶部(对 stack 而言)或队头(对 queue,priority_queue 而言)的元素的引用。
  • pop:删除一个元素。

stack

stack就是“栈”。栈是一种后进先出的元素序列,访问和删除都只能对栈顶的元素(即最后一个被加入栈的元素)进行,并且元素也只能被添加到栈顶。栈内的元素不能访问。如果一定要访问栈内的元素,只能将其上方的元素全部从栈中删除,使之变成栈顶元素才可以。
{% note primary %}
使用时包含stack头文件
在默认情况下,stack 就是用 deque 实现的。当然,也可以指定用 vector 或 list 实现。
{% endnote %}

成员函数解释
void pop();弹出(即删除)栈顶元素
T & top();返回栈顶元素的引用。通过此函数可以读取栈顶元素的值,也可以修改栈顶元素
void push (const T & x);将 x 压入栈顶

queue

queue 就是“队列”。队列是先进先出的,和排队类似。队头的访问和删除操作只能在队头进行,添加操作只能在队尾进行。不能访问队列中间的元素。

使用时包含queue头文件
queue 可以用 list 和 deque 实现,默认情况下用 deque 实现。
queue 同样也有和 stack 类似的 push、pop、top 函数。区别在于,queue 的 push 发生在队尾,pop 和 top 发生在队头。

priority_queue

priority_queue 是“优先队列”。它和普通队列的区别在于,优先队列的队头元素总是最大的——即执行 pop 操作时,删除的总是最大的元素;执行 top 操作时,返回的是最大元素的引用。

  • 使用时包含queue头文件
  • queue 可以用 list 和 deque 实现,默认情况下用 deque 实现。
  • queue 同样也有和 stack 类似的 push、pop、top 函数。区别在于,queue 的 push 发生在队尾,pop 和 top 发生在队头。
    priority_queue 可以用 vector 和 deque 实现,默认情况下用 vector 实现。
  • priority_queue 默认的元素比较器是 less 。也就是说,在默认情况下,要放入 priority_queue 的元素必须是能用“<”运算符进行比较的,而且 priority _queue 保证以下条件总是成立:对于队头的元素 x 和任意非队头的元素 y,表达式“x<y”必为 false。priority_queue 的第三个类型参数可以用来指定排序规则。

string类

  1. 创建string
string s();//s=""
string s1("123");//s1="123"
string s2(2,'a');//s2="aa"
string s3("12345670",1,3);//s4="234"

  1. 赋值
string s1,s2;
s1 = "Hello,world";
s2 = 'a';//

string的成员函数assig()也可以用来赋值,约等于’=’;

  1. 求长度

s.length()或者s.size()

  1. 字符串连接
s1.append(s2);
s1.append(s2,1,2);
s1.append(3,'a');
  1. string对象比较

可以用< <= == != >= >
可以用int compare()函数,返回值:0->相等;

string s1("hello"), s2("hello, world");
int n = s1.compare(s2);
n = s1.compare(1, 2, s2, 0, 3);  //比较s1的子串 (1,2) 和s2的子串 (0,3)
n = s1.compare(0, 2, s2);  // 比较s1的子串 (0,2) 和 s2
n = s1.compare("Hello");
n = s1.compare(1, 2, "Hello");  //比较 s1 的子串(1,2)和"Hello”
n = s1.compare(1, 2, "Hello", 1, 2);  //比较 s1 的子串(1,2)和 "Hello" 的子串(1,2)

  1. 求子串

string substr()函数

string s1 = "hello";
string s2 = s1.substr(2, 4);  // s2 = "llo"
s2 = s1.substr(1);  // s2 = "ello"

  1. 交换

swap()

string s1("123"),s2("456");
s1.swap(s2);

  1. 查找
  • find:从前往后查找子串或字符出现的位置。
  • rfind:从后往前查找子串或字符出现的位置。
  • find_first_of:从前往后查找何处出现另一个字符串中包含的字符。例如:s1.find_first_of(“ab”); //查找s1中第一次出现"ab"中任一字符的位置
  • find_last_of:从后往前查找何处出现另一个字符串中包含的字符。
  • find_first_not_of:从前往后查找何处出现另一个字符串中没有包含的字符。
  • find_last_not_of:从后往前查找何处出现另一个字符串中没有包含的字符
  1. 替换子串

replace()

string s1("hahahaha");
s1.replace(1, 3, "123456", 2, 4);  //用 "123456" 的子串从2开始的4个元素 替换 s1 的子串(1,3)
cout << s1 << endl;  //输出 h3456haha
string s2("xixixi");
s2.replace(2, 3, 5, '0');  //用 5 个 '0' 替换子串(2,3)
cout << s2 << endl;  //输出 xi00000xi
int n = s2.find("OOOOO");  //查找子串 "00000" 的位置,n=2
s2.replace(n, 5, "111");  //将子串(n,5)替换为"XXX"
cout << s2 < < endl;  //输出 xi1110xi

  1. 删除字串

erase()成员函数可以删除 string 对象中的子串,返回值为对象自身的引用。

s1.erase(1,3);//范围删除
s1.erase(2);//删除索引2之后的所有元素

  1. 插入字符串
string s1("hihihi"), s2("00");
s1.insert(2, "123");  //在下标 2 处插入字符串"123",s1 = "hi123hihi"
s1.insert(3, s2);  //在下标 2 处插入 s2 , s1 = "hi00123hihi"
s1.insert(3, 5, '1');  //在下标 3 处插入 5 个 'X',s1 = "hi0111110123hihi"

  1. 流转换
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main()
{
    string s("hi 99 5.21 hello k");
    istringstream istrStream(s); //建立s到istrStream的联系
    string s1, s2;
    int n;  double d;  char c;
    istrStream >> s1 >> n >> d >> s2 >> c; //把s的内容当做输入流进行读取
    ostringstream ostrStream;
    ostrStream << s1 << endl << s2 << endl << n << endl << d << endl << c <<endl;//此时所有的值都已经按空格分配
    cout << ostrStream.str();//将ostrStream中的所有值显示出来
    system("pause");
    return 0;
}

  1. STL操作字符串
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
int main()
{
    string s("afgcbed");
    string::iterator p = find(s.begin(), s.end(), 'c');
    if (p!= s.end())
        cout << p - s.begin() << endl;  //输出 3
    sort(s.begin(), s.end());
    cout << s << endl;  //输出 abcdefg
    system("pause");
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三喂树屋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值