【C++学习笔记】C++提高编程(泛型编程、STL)

模板

1.1 模板的概念

模板就是建立一个通用的模具,提高复用性

1.2 函数的模板

C++提供两种模板机制:函数模板类模板

1.2.1 函数模板的语法

语法:

template<typename/class T>
函数声明和定义

示例:

template<typename T>
void mySwap(T &a, T &b){
	T temp=a;
	a=b;
	b=temp;
}
//两种方法使用模板
// 1、自动匹配
mySwap(a,b);
// 2、显示指定类型
mySwap<int>(a,b);

1.2.2 函数模板注意事项

注意事项:

  • 自动类型拥导,必须推导出一致的数据类型T,才可以使用
  • 模板必须要确定出T的数据类型,才可以使用

1.2.3 普通函数和函数模板的区别

普通函数与函数模板区别:

  • 普通函数调用时可以发生自动类型转换(隐式类型转换)
  • 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
  • 如果利用显示指定类型的方式,可以发生隐式类型转换

1.2.4 普通函数和函数模板的调用规则

调用规则如下:

  • 1.如果函数模板和普通函数都可以实现,优先调用普通函数
  • 2.可以通过空模板参数列表来强制调用函数模板 例如:mySwap<>
  • 3.函数模板也可以发生重载
  • 4.如果函数模板可以产生更好的匹配,优先调用函数模板

1.2.5 模板的局限性

需要对特定的类型提供具体化的操作

template<> bool myComp(Person &a, Person &b){
	if(a.name==b.name)
		return true;
	else 
		return false;
}

1.3 类模板

语法:

template<typename/class T>

示例:

template<class NameType,class AgeType>
class Person{
public:
	Person(NameType name,AgeType age){
		this.name=name;
		this->age=age;
	}
	NameType name;
	AgeType age;
}
Person<string,int>p1("Tom",10);

1.3.1 类模板和函数模板的区别

类模板与函数模板区别主要有两点:

  • 1.类模板没有自动类型推导的使用方式,只能显示指定
template<class NameType,class AgeType>

Person<string,int> p("",10);
  • 2.类模板在模板参数列表中可以有默认参数
template<class NameType,class AgeType = int> //默认参数

Person<string> p("",10); //类模板在模板参数列表中可以有默认参数

1.3.2 类模板中的成员函数创建时机

类模板中成员函数和普通类中成员函数创建时机是有区别的:

  • 普通类中的成员函数一开始就可以创建
  • 类模板中的成员函数在调用时才创建

1.3.4 类模板对象做函数参数

一共有三种传入方式:

template<class T1,class T2>
class Person{
public:
	Person(T1 name,T2 age):m_Name(name),m_Age(age){}
	T1 m_Name;
	T2 m_Age;
}
  • 1.指定传入的类型:直接显示对象的数据类型
void printPerson(Person<string,int> &p){};

Person<string,int> p("",10);
printPerson(p);
  • 2.参数模板化:将对象中的参数变为模板进行传递
template<class T1,class T2>
void printPerson(Person<T1,T2> &p){};

Person<string,int> p("",10);
printPerson(p);
  • 3.整个类模板化:将这个对象类型模板化进行传递
template<class T>
void printPerson(T &p){};

Person<string,int> p("",10);
printPerson(p);

1.3.5 类模板与继承

当类模板碰到继承时,需要注意一下几点:

  • 当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中的的类型。
  • 如果不指定,编译器无法给子类分配内存
  • 如果想灵活指定出父类中T的类型,子类也需变为类模板
template<class T>
class Base{
	T m;
}
class Son: public Base{} //错误,必须指定父类中T的数据类型,才能继承给子类
class Son: public Base<int>{} //正确

template<class T1,class T2>
class Son2 : public Base<T1>{
	T2 obj;
}
Son2<int,char> s2;

1.3.6 类模板成员函数的类外实现

template<class T1,class T2>
class Person{
public:
	T1 m_Name;
	T2 m_Age;
}
//构造函数的类外实现
template<class T1,class T2>
Person<T1,T2>::Person(T1 name,T2 age){}	//Person<T1,T2>表示是类模板

//成员函数的类外实现
template<class T1,class T2>
void Person<T1,T2>::showPerson(){}

1.3.7 类模板分文件编写

问题:
类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到解决

解决方式

  • 1:直接包含.cpp源文件
  • 2:将声明和实现写到同一个文件中,并更改后缀名为**.hpp**,hpp是约定的名称,并不是强制

1.3.8 类模板与友元

  • 全局函数类内实现:直接在类内声明友元即可
//通过全局函数 打印Person
template<class T1,class T2>
class Person{
	//全局函数 类内实现
	friend void showPerson(Person<T1,T2> p){
		cout<<p.m_Name<<p.m_Age<<endl;
	}
	friend void showPerson2<>(Person<T1,T2> p); //加空模板的参数列表
private:
	T1 m_Name;
	T2 m_Age;
}
  • 全局函数类外实现:需要提前让骗译器知道全局函数的存在
//通过全局函数 打印Person
//提前让骗译器知道Person类的存在
template<class T1,class T2>
class Person//提前让骗译器知道showPerson2的存在
template<class T1,class T2>
void showPerson2(Person<T1,T2> p){
	cout<<p.m_Name<<p.m_Age<<endl;
}

template<class T1,class T2>
class Person{
	friend void showPerson2<>(Person<T1,T2> p); //加空模板的参数列表
private:
	T1 m_Name;
	T2 m_Age;
}

STL

2.1 STL初识

1.1 STL基本概念

  1. 标准模板库,建立数据结构与算法的一套标准
  2. 分为:容器、算法、迭代器

1.2 STL六大组件

容器、算法、迭代器、仿函数、适配器、空间配置器

  1. 容器:各种数据结构,如vector、list、deque、set、map等,用来存放数据。
  2. 算法:各种常用的算法,如sort、find、copy、for_each等
  3. 迭代器:扮演了容器与算法之间的胶合剂。
  4. 仿函数:行为类似函数,可作为算法的某种策略
  5. 适配器:一种用来修饰容器或者仿函数或迭代器接口的东西。
  6. 空间配置器:负责空间的配置与管理

2 STL容器

2.1 string

本质上是一个类,内部封装了char*

2.1.1 string构造函数

string();
string(const string &s);
string(const char* s);
string(int n,char c);

2.1.2 string赋值操作
string& operator=(const char* s); //char*类型字符串 赋值给当前的字符串
string& operator=(const string &s); //把字符串s赋给当前的字符串
string& operator=(char c); //字符赋值给当前的字符串
string& assign(const char *s); //把字符串s赋给当前的字符串
string& assign(const char *s,int n): //把字符串s的前n个字符赋给当前的字符串
string& assign(const string &s); //把字符串s赋给当前字符串
string& assign(int n, char c); //用n个字符c赋给当前字符串
2.1.3 string拼接操作
string& operator+=[const char* str); //重载+=操作符
string& operator+=(const char c); //重载+=操作符
string& operator+=(const string& str); //重载+=操作符
string& append(const char *s); //把字符串s连接到当前字符串结尾
string& append(const char *s,int n); //把字符串s的前n个字符连接到当前字符串结尾
string& append(const string &s); //同operator+=(const string& str)
string& append(const string&s,int p0s,int n); //字符串s中从p0s开始的n个字符连接到字符串结尾
2.1.4 string查找和替换

rfind: 从右往左查找; find: 从左往右查找

int find(const string& str,int pos=0) const; //查找str第一次出现位置,从pos开始查找
int find(const char* s, int pos =0) const; //查找s第一次出现位置,从pos开始查找
int find(const char* s,int pos, int n) const; //从pos位置查找s的前n个字符第一次位置
int find(const char c,int pos =0) const; //查找字符c第一次出现位置
int rfind(const string& str,int pos = npos); //查找str最后一次位置,从pos开始查找
const;int rfind(const char*s,int pos =npos) const; //查找s最后一次出现位置,从pos开始查找
int rfind(const char*s,int pos,int n) const; //从pos查找s的前n个字符最后一次位置
int rfind(const char c,int pos=8) const;  //查找字符(最后一次出现位置

string& replace(int pos,int n,const string& str); //替换从pos开始n个字符为字符串str
string& replace(int pos,int n,const char* s); //替换从pos开始的n个字符为字符串s
2.1.5 string比较

比较ASCII值,=:0,>:1,<:-1

int compare(const string &s)const; //与字符串s比比较
int compare(constchar *s)const; //与字符串s比较
2.1.6 string字符存取
char& operator[](int n); //通过口方式取字符
char& at(int n); //通过at方法获取字符
2.1.7 string插入和删除
string& insert(int pos,const char* s); //插入字符串
string& insert(int pos, const string& str); //插入字符串

string& insert(int pos,int n, char c); //在指定位置插入n个字符c
string& erase(int pos,int n=npos); //删除从Pos开始的n个字符
2.1.8 string字串
string substr(intpos=8,intn=npos)const; //返回由pos开始的n个字符组成的字符串

2.2 vector

与数组很相似,但可以动态扩展,支持随机存取
容器:vector
算法:for_each
迭代器:vector<int>::iterator

vector.begin() //第一个元素的位置
vector.end() //最后一个元素的下一个位置

void myPrint(int val){
	cout<<val<<endl;
}
for_each(v.begin(),v.end(),myPrint) //遍历输出
2.2.1 vector构造函数
vector<T> v; //采用模板实现类实现,默认构造函数
vector(v.begin(),v.end()); //将v[begin(), end()区间中的元素拷贝给本身。
vector(int n, elem); //构造函数将n个elem拷贝给本身
vector(const vector &vec); //拷贝构造函数。
2.2.2 vector赋值
vector& operator=(const vector &vec);//重载等号操作符
assign(beg, end); //将[beg, end)区间中的数据拷贝赋值给本身。
assign(n, elem); //将n个elem拷贝赋值给本身。
2.2.3 vector容量和大小
empty(); //判断容器是否为空
capacity(); //容器的容量
size(); //返回容器中元素的个数
resize(int num); //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。//如果容器变短,则末尾超出容器长度的元素被删除。//重新指定容器的长度为num,若容器变长,则以elem值填充新位置。
resize(int num,elem);//如果容器变短,则末尾超出容器长度的元素被删除
2.4.4 vector插入和删除
push_back(ele); //尾部插入元素ele
pop_back(); //删除最后一个元素
insert(const iterator pos, ele); //迭代器指向位置pos插入元素ele
insert(const iterator pos,int count,ele);//迭代器指向位置p0s插入count个元素ele
erase(const iterator pos); //删除迭代器指向的元素
erase(const_iterator start,const_iterator end);//删除选代器从start到end之间的元素
clear();
//删除容器中所有元素
2.2.5 vector数据存取
at(int idx); //返回索引idx所指的数据
operator[idx]; //返回索引idx所指的数据
front(); //返回容器中第一个数据元素
back(); //返回容器中最后一个数据元素
2.2.6 vector互换容器

swap(vec); //将vec与本身的元素互换
巧用swapvector<int>(v).swap(v); //收缩容量空间
在这里插入图片描述

2.2.7 vector预留空间

减少vector再动态扩展容量时的扩展次数
reserve(int len); //容器预留len个元素长度,预留位置不初始化,元素不可访问

2.3 deque

双端数组,可以再头部进行插入删除操作

2.3.1 deque构造函数
deque<T> deqT; //默认构造形式
deque(beg, end); //构造函数将[beg,end)区间中的元素拷贝给本身,
deque(n, elem); //构造函数将n个elem拷贝给本身
deque(const deque &deg); //拷贝构造函数
2.3.2 deque赋值操作
deque& operator:(const deque &deg); //重载等号操作符
assign(beg, end); //将[beg, end)区间中的数据拷贝赋值给本身,
assign(n, elem); //将n个elem拷贝赋值给本身,
2.3.3 deque大小容量
deque.empty(); //判断容器是否为空
deque.size(); //返回容器中元素的个数
deque.resize(num); //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。//如果容器变短,则末尾超出容器长度的元素被删除。
deque.resize(num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置,//如果容器变短,则末尾超出容器长度的元素被删除。
2.3.4 deque插入删除

两端插入操作:

push_back(elem); //在容器尾部添加一个数据
push_front(elem); //在容器头部插入一个数据
pop_back(); //删除容器最后一个数据
pop_front(); //删除容器第一个数据

指定位置操作:

insert(pos,elem); //在pos位置插入一个elem元素的拷贝,返回新数据的位置。
insert(pos,n,elem); //在pos位置插入n个elem数据,无返回值,
insert(pos,beg,end); //在pos位置插入[beg,end)区间的数据,无返回值。
clear(); //清空容器的所有数据
erase(beg,end); //删除[beg,end)区间的数据,返回下一个数据的位置。
erase(pos); //删除pos位置的数据,返回下一个数据的位置,
2.3.5 deque数据存取
at(int idx); //返回素引idx所指的数据
operator[]; //返回索引idx所指的数据
front(); //返回容器中第一个数据元素
back(); //返回容器中最后一个数据元素
2.3.6 deque排序

sort(iterator beg,iterator end); //对beg和end区间内元素进行排序

2.4 stack

栈容器,先进后出

2.4.1 stack常用接口
构造函数:
stack<T> pstk; //stack采用模板类实现,stack对象的默认构造形式
stack(const stack &stk); //拷贝构造函数

赋值操作:
stack& operator=(const stack &stk); //重载等号操作符

数据存取:
push(elem); //向栈顶添加元素
pop(); //从栈顶移除第一个元素
top(); //返回栈顶元素

大小操作:
empty(); //判断堆栈是否为空
size(); //返回栈的大小

2.4 queue

队列容器,先进先出,队头出数据,队尾入数据

2.4.1 queue常用接口
构造函数:
queue<T> que; //queue采用模板类实现,queue对象的默认构造形式
queue(const queue &que); //拷贝构造函数

赋值操作:
queue& operator=(const gueue &que); //重载等号操作符

数据存取:
push(elem); //往队尾添加元素
pop(); //从队头移除第一个元素
back(); //返回最后一个元素
front(); //返回第一个元素

大小操作:
empty(); //判断堆栈是否为空
size(); //返回栈的大小

2.5 list

链表容器,链式存储,STL中是双向循环链表
插入删除操作不会造成原有list迭代器失效
在这里插入图片描述

2.5.1 list构造函数
list<T> lst; //list采用采用模板类实现,对象的默认构造形式:
list(beg,end); //构造函数将[beg, end)区间中的元素拷贝给本身
list(n,elem); //构造函数将n个elem拷贝给本身。
list(const list &lst); //拷贝构造函数。
2.5.2 list赋值和交换
list& operator=(const list &lst); //重载等号操作符
assign(beg, end); //将[beg, end)区间中的数据拷贝赋值给本身
assign(n, elem); //将n个elem拷贝赋值给本身,

swap(lst); //将lst与本身的元素互换。
2.5.3 list大小
size(); //返回容器中元素的个数
empty(); //判断容器是否为空
resize(num); //重新指定容器的长度为num,若容器变长,则以默认值填充新位置//如果容器变短,则未尾超出容器长度的元素被删除。
resize(num, elem); //重新指走容器的长度为num,若容器变长,则以elem值填充新位置,//如果容器变短,则末尾超出容器长度的元素被删除。
2.5.4 list插入删除
push_back(elem);//在容器尾部加入一个元素
pop_back();//别除容器中最后一个元素
push_front(elem);//在容器开头插入一个元素
pop_front();//从容器开头移除第一个元素

insert(pos,elem);//在pos位置插elem元素的拷贝,返回新数据的位置。insert(pos,n,elem);//在pOS位置插入n个elem数据,无返回值,insert(pos,beg,end);//在pos位置插入[beg,end)区间的数据,无返回值。

clear();//移除容器的所有数据
erase(beg,end);//删除[beg,end)区间的数据,返回下一个数据的位置,
erase(pos);//删除pos位置的数据,返回下一个数据的位置:
remove(elem);//删除容器中所有与elem值匹配的元素,
2.5.5 list数据存取
front(); //返回第一个元素。
back(); //返回最后一个元素。
2.5.6 list排序和反转
reverse(); //反转链表
sort(); //链表排序

2.6 set / multiset

集合容器,自动排序,底层用二叉树实现
set:不允许重复元素
multiset:允许重复元素

2.6.1 set构造、赋值、插入
set<T> st; //默认构造函数:
set(const set &st); //拷贝构造函数
set& operator=(const set &st); //重载等号操作符
2.6.2 set大小和交换
size(); //返回容器中元素的数目
empty(); //判断容器是否为空
swap(st); //交换两个集合容器
2.6.3 set插入删除
insert(elem); //在容器中插入元素 ==插入只有这一种==

clear(); //清除所有元素
erase(pos); //M除pos迭代器所指的元素,返回下一个元素的迭代器。
erase(beg, end); //删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。
erase(elem); //删除容器中值为elem的元素。
2.6.4 set查找和统计
find(key); //查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end();
count(key); //统计key的元素个数
2.6.5 pair对组创建

成对出现的数据

pair<type,type>p(value1,value2 );
pair<type,type>p=make_pair( value1,value2 );
2.6.6 set排序

如何改变排序规则,利用仿函数

存放内置数据类型:
class MyCompare{
public:
	bool operator()(int v1,int v2){
		return v1>v2;
	}
}
set<int,MyCompare> s;
s.insert(10);
...

存放自定义数据类型:
class Person{
public:
	string m_Name;
	int m_Height;
	...
}
class MyCompare{
public:
	bool operator()(const Person &p1,const Person &p2){
		return p1.m_Age>p2.m_Age;
	}
}

set<Person,MyCompare> s;
s.insert(p);

2.7 map / multimap

2.7.1 map基本概念

map中数据成对出现
map:不允许有重复Key
multimap:允许有重复Key
成对出现的数据

2.7.2 map构造和赋值
map<T1,T2> mp; //map默认构造函数:
map(const map &mp); //拷贝构造函数

map& operator=(const map &mp); //重载等号操作符
2.7.3 map大小和交换
size(); //返回容器中元素的数目
empty(); //判断容器是否为空

swap(st); //交换两个集合容器
2.7.4 map插入和删除
insert(elem); //在容器中插入元素。
clear(); //清除所有元素
erase(pos); //删除pos迭代器所指的元素,返回下一个元素的迭代器。
erase(beg, end); //M除区间[beg,end)的所有元素,返回下一个元素的迭代器
erase(key);//删除容器中值为key的元素。
2.7.5 map查找和统计
find(key); //查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回 set.end();
count(key); //统计key的元素个数
2.7.6 map大小和交换
size(); //返回容器中元素的数目
empty(); //判断容器是否为空

swap(st); //交换两个集合容器
2.7.3 map排序

默认按key从小到大,如何改变排序规则,利用仿函数

class MyCompare{
public:
	bool operator()(int v1,int v2){
		return v1>v2;
	}
}

map<int, int, MyCompare> m;
2.7.3 map大小和交换
size(); //返回容器中元素的数目
empty(); //判断容器是否为空

swap(st); //交换两个集合容器

3 STL-函数对象

3.1 函数对象

概念:
重载函数调用操作符的类,其对象常称为函数对象
函数对象使用重载的()时,行为类似函数调用,也叫仿函数
本质:
函数对象(仿函数)是一个类,不是一个函数

  1. 函数对象像普通函数一样调用,可以有返回值
class MyAdd{
public:
	int operator ()(int x,int y){
		return x+y;
	}
}

MyAdd myadd;
cout<< myadd(1,2) <<endl;
  1. 函数对象超出普通函数的概念,可以有自己的状态
class MyPrint{
public:
	MyPrint(){
		this->count=0;
	}
	void operator ()(string str){
		count << str <<endl;
		this->count++;
	}
	int count; //内部自己的状态
}
  1. 函数对象作为参数传递
class MyPrint{
public:
	void operator ()(string str){
		count << str <<endl;
		this->count++;
	}
}
void doPrint(MyPrint &mp, string str){
	
}
MyPrint mp;
doPrint(mp,"hello");

3.2 谓词

概念:
返回bool类型的仿函数称为谓词
如果operator()接受一个参数,那么叫做一元谓词
如果operator()接受两个参数,那么叫做二元谓词

//一元谓词
class GreaterFive{
public:
	bool operator()(int val){
		return val > 5;
	}
}

voidi teste1(){
	vector<int>v;
	for(inti=0;i<10;i++)
		v.push back(i);
	vector<int>::iterator it =find(v.begin(),v.end(),GreaterFive());
	if(it ==v.end())
		cout<<"没找到!"<<endl;
	else 
		cout<<“找到:"<<*it<< end1;
}

//二元谓词
class MyCompare{
public:
	bool operator()(int val1, int val2){
		return val1 > val2;
	}
}
sort(v.begin(),v.end,MyCompare());

3.3 内建函数对象

概念:
STL内建了一些函数对象
分类:
算术仿函数
关系仿函数
逻辑仿函数
用法:
这些仿函数所产生的对象,用法和一般函数完全相同使用内建函数对象,需要引入头文#include<functional>

3.3.1 算术仿函数

功能描述:
实现四则运算
其中negate是一元运算,其他都是二元运算

仿函数原型:

 - template<class T>T plus<T> //加法仿函数
 - template<class T>T minus<T> //减法仿函数
 - template<class T>T multiplies<T> //乘法仿函数
 - template<class T>T divides<T> //除法仿函数
 - template<class T>T modulus<T> //取模仿函数
 - template<class T>T negate<T> //取反仿函数

示例:

negate<int> n;
n(50)

plus<int> p;
p(1,2);
3.3.2 关系仿函数

功能描述:
实现关系对比
仿函数原型:

 - template<class T> bool equal to<T> //等于
 - template<class T> bool not equal to<T> //不等于
 - template<class T> bool greater<T> //大于
 - template<class T> bool greater equal<T> //大于等于
 - template<class T> bool less<T> //小于
 - template<class T> bool less equal<T> //小于等于

示例:

sort(v.begin(),v.end,greater<int>());
3.3.3 逻辑仿函数

实现逻辑运算

 - template<class T> bool logical_and<T> //逻辑与
 - template<class T>bool logical_or<T> //逻辑或
 - template<class T>bool logical_not<T> //逻辑非

4 STL-常见算法

算法主要是由头文件<algorithm> <functional> <numeric>组成。

<algorithm> 是所有STL头文件中最大的一个,范围涉及到比较、 交换、查找、遍历操作、复制、修改等等
<numeric> 体积很小,只包括几个在序列上面进行简单数学运算的模板函数
<functional> 定义了一些模板类,用以声明函数对象。

4.1 遍历算法

for_each 遍历容器
transform 搬运容器到另一个容器中

4.1.1 for_each

for each(iterator beg,iterator end, _func);
// 遍历算法 遍历容器元素
// beg 开始迭代器
// end 结束迭代器
// func 函数或者函数对象

4.1.2 transform

功能描述:
搬运容器到另一个容器中函数原型:
transform(iterator begl,iterator end1, iterator beg2,_func);
//beg1 源容器开始迭代器
//end1 源容器结束迭代器
//beg2 目标容器开始迭代器
//_func 函数或者函数对象

4.2 查找算法

算法简介:

  • find 查找元素
  • find_if 按条件查找元素
  • adjacent_find 查找相邻重复元素
  • binary_search 二分查找法
  • count 统计元素个数
  • count_if 按条件统计元素个数

4.3 排序算法

算法简介:

  • sort 对容器内元素进行排序
  • random_shuffle 洗牌 指定范围内的元素随机调整次序
  • merge 容器元素合并,并存储到另一容器中
  • reverse 反转指定范围的元素

4.4 拷贝和替换算法

算法简介:

  • copy 容器内指定范围的元素拷贝到另一容器中
  • replace 将容器内指定范围的旧元素修改为新元素
  • replace_if 容器内指定范围满足条件的元素替换为新元素
  • swap 互换两个容器的元素

4.5 算术生成算法

算法简介:

  • accumulate 计算容器元素累计总和
  • fill 向容器中添加元素

4.6 集合算法

算法简介:

  • set_intersection 求两个容器的交集
  • set_union 求两个容器的并集
  • set_difference 求两个容器的差集
  • 22
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

一往而情深

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

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

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

打赏作者

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

抵扣说明:

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

余额充值