C++语法[高级]

C++语法(高级)

模板

C++的另一种编程思想:泛型编程;主要利用的技术就是模板
模板就是建立通用的模具,提高复用性。
模板的特点:

  • 模板不可以直接使用,它只是个框架
  • 模板的通用并不是万能的

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

函数模板

函数模板作用:建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表。

函数模板基本语法

语法:

  1. template<typename T>
  2. 函数声明或定义

解释:

  1. template – 声明创建模板
  2. typename – 表明其后面的符号是一种数据类型;可以用class代替
  3. T – 通用的数据类型,名称可以替换,通常为大写字母

使用自动模板的方式:

  1. 自动类型推导
  2. 显示指定类型
#include <iostream>
using namespace std;

template<typename T>
void swapData(T &a, T &b)
{
    T tmp = a;
    a = b;
    b = tmp;
}

void test01()
{
    //1.自动类型推导
    int a = 2;
    int b = 3;
    swapData(a,b);
    cout << "a: " << a << endl;
    cout << "b: " << b << endl;
}
void test02()
{
    //2.显示指定类型
    float a = 1.1;
    float b = 2.1;
    swapData<float>(a, b);
    cout << "a: " << a << endl;
    cout << "b: " << b << endl;
}

int main()
{
    test01();
    cout << "--------" << endl;
    test02();
}

函数模板注意事项

  • 自动类型推导,必须推导出一致的数据类型T,才可以使用
  • 模板必须要确定T的类型,才可以使用
#include <iostream>
using namespace std;

template<typename T>
void swapData(T &a, T &b)
{
    T tmp = a;
    a = b;
    b = tmp;
}
void test01()
{
    int a = 2;
    char c = 'c';
    swapData(a,c);	// 编译器会提示错误,不能推导出一致的数据类型
}

template<typename T>
void func()		//此函数没有用到数据类型T
{
	cout << "func 调用" << endl;
}
void test02()
{
	func();		//编译器会提示错误,没有确定T的类型
	func<int>();//此行代码是正确的,因为告诉编译器T是个int(or float/char等)
}

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

案例:利用函数模板封装一个排序函数,可以对不同数据类型的数组进行排序;规则:从大到小,排序为选择排序;分别利用char数组int数组进行测试

#include <iostream>
using namespace std;

template<typename T>
void swapData(T &a, T &b)
{
    T tmp = a;
    a = b;
    b = tmp;
}

template<typename T>
void mySort(T &arr)
{
    int size = sizeof(arr)/sizeof(arr[0]);
    for (int i = 0; i < size-1; i++)
    {
        int max = i;
        for (int j = i+1; j < size; j++)
        {
            if (arr[i] < arr[j])
            {
                max = j;
            }
        }
        if (max != i)
        {
            swapData(arr[max], arr[i]);
        } 
    }
}

template<typename T>
void printArray(T &arr)
{
    int num = sizeof(arr)/sizeof(arr[0]);
    for (int i = 0; i < num; i++)
    {
        cout << arr[i] << " ";
    }
    cout << endl; 
}

void test01()
{
    int arr1[] = {2, 3, 6, 1, 7, 8};
    mySort(arr1);
    printArray(arr1);       // 输出结果:8 7 6 2 3 1
}
void test02()
{
    char arr2[] = "badcfe";
    mySort(arr2);
    printArray(arr2);       // 输出结果:e b f d c a 
}

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

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

区别:

  • 普通函数调用时可以发生自动类型转换(隐式类型转换)
  • 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换。
  • 函数模板调用时,如果利用显示指定类型的方式,可以发生隐式类型转换。
#include <iostream>
using namespace std;

// 普通函数
int myAdd01(int a, int b)
{
    return a + b;
}
// 模板函数
template<typename T>
int myAdd02(T a, T b)
{
    return a + b;
}

void test01()
{
    int a = 10, b = 20;
    cout << myAdd01(a, b) << endl;  // 输出结果:30; 没有发生隐式类型转换
    // 测试 隐式转换
    char c = 'c';   // c -> 99
    cout << myAdd01(a, c) << endl;  // 输出结果:109; 自动将'c'隐式的转换为整型

    // 自动类型推导
    cout << myAdd02(a, c) << endl;  // 编译器会提示报错,因为不会发生隐式类型转换,c是char,a是int,所以会报错
    // 显示指定类型
    cout << myAdd02<int>(a,c) << endl; // 输出结果:109; 显示将char转换为int
}

int main()
{
    test01();
}

普通函数与函数模板调用规则

调用规则如下:

  1. 如果普通函数和函数模板都可以实现,优先调用普通函数
  2. 可以通过空模板参数列表来强制调用函数模板
  3. 函数模板可以发生重载
  4. 如果函数模板可以产生更好的匹配,优先调用函数模板
#include <iostream>
using namespace std;

void myPrint(int a, int b)
{
    cout << "调用的普通函数" << endl;
}

template<typename T>
void myPrint(T a, T b)
{
    cout << "调用的函数模板" << endl;
}

template<typename T>
void myPrint(T a, T b, T c)     // 函数模板重载
{
    cout << "调用重载的函数模板" << endl;
}

void test01()
{
    int a = 10, b = 20;
    myPrint(a, b);      // 输出:调用的普通函数
    // 通过空模板参数列表,强制调用函数模板
    myPrint<>(a, b);    // 输出:调用的函数模板
    // 函数模板重载
    myPrint(a, b, 30);
    // 如果函数模板可以产生更好的匹配,优先调用函数模板
    char c1='c', c2='p';
    myPrint(c1, c2);    // 输出: 调用的函数模板;如果调用myPrint(int a, int b),需要做隐式类型转换,所以会调用函数模板而不是普通函数
}

int main()
{
    test01();   
}

模板的局限性

局限性:模板的通用性并不是万能的
例如:

template<typename T>
void func01(T a, T b)
{
	a = b;
}

如果上述代码传入的参数a、b是数组,那么赋值操作就无法实现了。

template<typename T>
void func02(T a, T b)
{
	if(a > b)
	{
		/* code */
	}
}

如果上述代码传入的参数a、b是自定义数据类型(例如:Person),那么就无法正常运行了。

因为这种问题,C++提供来模板重载,来为特定的类型提供具体化的模板

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

class Person
{
public:
    string m_Name;
    int m_Age;

    Person(string name, int age)
    {
        this->m_Age = age;
        this->m_Name = name;
    }
};

template<typename T>
bool myCompare(T &a, T &b)
{
    if (a == b)
    {
        return true;
    }
    else
    {
        return false;
    } 
}
// 重载模板,具体化模板
template<> bool myCompare(Person &p1, Person &p2)
{
    if (p1.m_Age == p2.m_Age && p1.m_Name == p2.m_Name)
    {
        return true;
    }
    else
    {
        return false;
    } 
}

void test01()
{
    int a=10, b=20;
    bool ret = myCompare(a, b);
    if (ret)
    {
        cout << "a == b" << endl;
    }
    else
    {
        cout << "a != b" << endl;
    }
}

void test02()
{
    Person p1("Andy", 18);
    Person p2("Andy", 18);
    bool res = myCompare(p1, p2);
    if (res)
    {
        cout << "p1 == p2" << endl;
    }
    else
    {
        cout << "p1 != p2" << endl;
    }
}

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

总结:

  • 利用具体化模板,可以解决自定义类型的通用化
  • 学习模板不是为了写模板,而是在STL中能够运用系统提供的模板

类模板

基本语法

类模板作用:建立一个通用类,类中的成员数据类型可以不具体制定,用一个虚拟的类型来代表。
语法:

  1. template<typename T> or template<class T>

Tips: 如果多个参数,可以这样写template<class T1, class T2, ...>
解释:

  1. template – 声明创建模板
  2. typename – 表明其后面的符号是一种数据类型;可以用class代替
  3. T – 通用的数据类型,名称可以替换,通常为大写字母
#include <iostream>
using namespace std;

template<class NameType, class AgeType>
class Person
{
public:
    NameType m_Name;
    AgeType m_Age;
    Person(NameType name, AgeType age)
    {
        this->m_Age = age;
        this->m_Name = name;
    }
};

void test01()
{
    Person<string, int> p1("Tom", 18);
    cout << p1.m_Name << "\t" << p1.m_Age << endl;
}

int main()
{
    test01();
}

类模板与函数模板区别

区别:

  • 类模板没有自动类型推导的使用方式(C++17中已新增“自动类型推导”功能
  • 类模板在模板参数列表中可以有默认参数
#include <iostream>
using namespace std;

template<class NameType, class AgeType=int>     // 模板参数列表有默认参数
class Person
{
public:
    NameType m_Name;
    AgeType m_Age;
    Person(NameType name, AgeType age)
    {
        this->m_Age = age;
        this->m_Name = name;
    }
    void showPerson()
    {
        cout << "name: " << this->m_Name << "age: " << this->m_Age << endl;
    }
};

void test01()
{
    Person<string, int> p1("Tom", 18);
    p1.showPerson();	// output: Tom	18

    Person p2("Andy", 20);          // 没报错,是因为本人使用的是C++17
    p2.showPerson();   	// output: Andy	20

    Person p3("Tom", "eighteen");   // 没报错,是因为本人使用的是C++17
    p3.showPerson();	// output: Tom	eighteen

    Person<string> p4("Andy", 28);  // 类模板在模板参数列表中可以有默认参数. 所以此时可以不写“int”
}

int main()
{
    test01();
    cout << "c++ version: " << __cplusplus << endl;     // 输出:201703 -> C++17
}

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

与普通类中成员函数创建时机的区别:

  • 普通类中的成员函数一开始就可以创建
  • 类模板中的成员函数在调用时才创建
#include <iostream>
using namespace std;

class Person1
{
public:
    void showPerson1()
    {
        cout << "class Person1 -> func " << endl;
    }
};
class Person2
{
public:
    void showPerson2()
    {
        cout << "class Person2 -> func " << endl;
    }
};

template<class T>
class MyClass
{
public:
    T obj;
    void func1()
    {
        obj.showPerson1();
    }
    void func2()
    {
        obj.showPerson2();
    }
};

int main()
{
    // 0.此时[main函数为空时]编译代码,是可以通过的。因为:类模板中的成员函数在调用时才创建
    // 1
    MyClass<Person1>m1;
    m1.func1();
    m1.func2();     // 此行代码编译时会报错:no member named 'showPerson2' in 'Person1'
    // 2
    MyClass<Person2>m2;
    m2.func1();     // 此行代码编译时会报错:no member named 'showPerson1' in 'Person2' 
    m2.func2();
}

类模板对象做函数参数

类模板实例化出的对象,向函数传参的方式:

  • 指定传入的类型:直接显示对象的数据类型
  • 参数模板化:将对象中的参数变为模板进行传递
  • 整个类模板化:将对象类型模板化进行传递
#include <iostream>
using namespace std;

template<class T1, class T2>
class Person
{
public:
    T1 m_Name;
    T2 m_Age;

    Person(T1 name, T2 age)
    {
        this->m_Name = name;
        this->m_Age = age;
    }
    void showPerson()
    {
        cout << "name: " << this->m_Name << "\tage: " << this->m_Age << endl;
    }
};
//指定传入的类型
void printPerson1(Person<string, int> &p)
{
    p.showPerson();
}
void test01()
{
    Person<string, int> p("Tom",18);
    printPerson1(p);
}
// 参数模板化
template<class T1, class T2>
void printPerson2(Person<T1, T2> &p)
{
    p.showPerson();
    cout << "T1的类型为:" << typeid(T1).name() << endl;
    cout << "T2的类型为:" << typeid(T2).name() << endl;
}
void test02()
{
    Person<string, int> p("Andy",18);
    printPerson2(p); 
}
// 整个类模板化
template<class T>
void printPerson3(T &p)
{
    p.showPerson();
    cout << "T的类型为:" << typeid(T).name() << endl;
}
void test03()
{
    Person<string, int> p("Tim", 20);
    printPerson3(p);
}

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

类模板与继承

类模板遇到继承时:
当子类继承的父类是类模板时,子类在声明的时候,需指定父类中的T的类型;
如果不指定,编译器无法给子类分配内存;
如果想灵活指定出父类中T的类型,子类也需变成类模板。

#include <iostream>
using namespace std;

template<class T>
class Base
{
public:
    T m;
};
// class Son:public Base {};    //  错误代码,必须要知道父类中的T类型,才能继承给子类
class Son:public Base<int>
{    
};
// 灵活指定父类中T的类型,子类也要变成类模板
template<class T1, class T2>
class Son2:public Base<T2>
{
public:
    T1 obj;
    Son2()
    {
        cout << "T1的类型为: " << typeid(T1).name() << endl;
        cout << "T2的类型为: " << typeid(T2).name() << endl;
    }
};

int main()
{
    Son s1;
    s1.m = 20;
    cout << "s1.m: " << s1.m << endl;

    Son2<int, string> s2;
    s2.obj = 10;
    cout << "s2.obj: " << s2.obj << endl;
    cout << typeid(s2.m).name() << endl;    // string
    s2.m = "hello";
    cout << "s2.m: " << s2.m << endl;
}

类模板成员函数类外实现

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

template<class T1, class T2>
class Person
{
public:
    T1 m_Name;
    T2 m_Age;
    Person(T1 name, T2 age);
    void showPerson();
};
//构造函数的类外实现
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
    this->m_Age = age;
    this->m_Name = name;
}
//类模板成员函数的类外实现
template<class T1, class T2>
void Person<T1, T2>::showPerson()
{
    cout << "name: " << this->m_Name << "\tage: " << this->m_Age << endl;
}

int main()
{
    Person p1("Tom",18);
    p1.showPerson();
}

类模板分文件编写

类模板分文件编写导致的问题
类模板中成员函数在调用时才会被创建,所以导致分文件编写时链接不到
解决

  • 方式1:直接包含.cpp文件(声明在.h文件,实现在.cpp文件,主函数文件直接导入.cpp文件)
  • 方式2:将声明和实现写在一个文件中,并更改后缀名为.hpp,hpp为约定的名称,并不是强制。

直接包含.cpp文件:

<person.h文件>
#pragma once
#include <iostream>
#include <string>
using namespace std;

template<class T1, class T2>
class Person
{
public:
    T1 m_Name;
    T2 m_Age;
    Person(T1 name, T2 age);
    void showPerson();
};
/*===============================================*/
<person.cpp文件>
#include "person.h"

//构造函数的类外实现
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
    this->m_Age = age;
    this->m_Name = name;
}
//类模板成员函数的类外实现
template<class T1, class T2>
void Person<T1, T2>::showPerson()
{
    cout << "name: " << this->m_Name << "\tage: " << this->m_Age << endl;
}
/*===============================================*/
<分文件编写.cpp>
#include <iostream>
#include <string>
using namespace std;
#include "person.cpp"

int main()
{
    Person p1("Andy",18);
    p1.showPerson();
}

用hpp后缀 <常用方法>

<person.hpp文件>
#include <iostream>
#include <string>
using namespace std;

template<class T1, class T2>
class Person
{
public:
    T1 m_Name;
    T2 m_Age;
    Person(T1 name, T2 age);
    void showPerson();
};
//构造函数的类外实现
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
    this->m_Age = age;
    this->m_Name = name;
}
//类模板成员函数的类外实现
template<class T1, class T2>
void Person<T1, T2>::showPerson()
{
    cout << "name: " << this->m_Name << "\tage: " << this->m_Age << endl;
}
/*===============================================*/
<分文件编写.cpp>
#include "person.hpp"

int main()
{
    Person p1("Tom",18);
    p1.showPerson();
}

类模板与友元

全局函数类内实现:直接在类内声明友元即可
全局函数类外实现:需要提前让编译器知道全局函数的存在
类内实现:

#include <iostream>
using namespace std;

template<class T1, class T2>
class Person
{
    //全局函数 类内实现
    friend void printPerson(Person<T1, T2> p)
    {
        cout << "类内实现--> name: " << p.m_Name << "\tage: " << p.m_Age << endl;
    }
public:
    T1 m_Name;
    T2 m_Age;
    Person(T1 name, T2 age)
    {
        this->m_Name = name;
        this->m_Age = age;
    }
};

int main()
{
    // 测试“全局函数类内实现”
    Person<string, int> p1("Andy", 18);
    printPerson(p1);
}

类外实现:

#include <iostream>
using namespace std;

template<class T1, class T2>
class Person;

//全局函数 类外实现
template<class T1, class T2>
void printPerson2(Person<T1, T2> p)
{
    cout << "类外实现--> name: " << p.m_Name << "\tage: " << p.m_Age << endl;
}

template<class T1, class T2>
class Person
{
    //全局函数 类外实现 [需要加空模板参数列表]
    friend void printPerson2<>(Person<T1, T2> p);
   
public:
    T1 m_Name;
    T2 m_Age;
    Person(T1 name, T2 age)
    {
        this->m_Name = name;
        this->m_Age = age;
    }
};


int main()
{
    // 测试“全局函数类外实现“
    Person<string, int> p2("Tom", 20);
    printPerson2(p2);
}

案例

STL

STL初识

为了建立数据结构和算法的一套标准,诞生了STL

STL基本概念

  • STL (Standard Template Library) 标准模板库
  • STL从广义上分为:容器(container)算法(algorithm)迭代器(iterator)
  • 容器算法之间通过迭代器连接
  • STL几乎所有的代码都采用了模板类或者模板函数

STL六大组件

分别为:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器

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

STL中容器、算法、迭代器

容器:置物之所也
STL容器就是运用最广泛的一些数据结构表现出来
常用的数据结构:数组、链表、树、栈、队列、集合、映射表等
容器分为序列式容器关联式容器

  • 序列式容器:强调值的排序,每个元素均有固定的位置
  • 关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系

算法:问题之解法也
有限的步骤,解决逻辑或数学上的问题,称之为算法
算法分为:质变算法非质变算法
质变算法:指运算过程中改变区间内的元素的内容。例如:拷贝、替换、删除等等
非质变算法:指运算过程中不会改变区间内的元素的内容。例如:查找、遍历、计数等等
迭代器:容器和算法之间的粘合剂
提供一种方法,使之能够依序访问容器中的元素,而又无需暴露该容器内部的实现方式。
每个容器都有自己专属的迭代器。
迭代器的使用类似于指针,初学可以将迭代器理解为指针。

迭代器种类:

种类功能支持的运算
输入迭代器对数据只读访问只读,支持++、==、!=
输出迭代器对数据只写访问只写,支持++
前向迭代器读写操作,并能向前推进迭代器读写,支持++、==、!=
双向迭代器读写操作,并能向前和向后操作读写,支持++、- -
随机访问迭代器读写操作,可以以跳跃的方式访问任意数据,功能最强的迭代器读写,支持++、- -、[n]、-n、<、<=、>、>=

常用的容器中的迭代器种类:双向迭代器、随机访问迭代器

vector

STL中最常用的容器:vector,可以理解为数组

vector存放内置数据类型

容器:vector
算法:for_each
迭代器:vector<int>::iterator

#include <vector>
#include <algorithm>
#include <iostream>

using namespace std;

void MyPrint(int val)
{
    cout << val << endl;
}

void test01()
{
    vector<int> v;      //创建一个vector容器(数组)
    v.push_back(10);    //向容器中插入数据
    v.push_back(20);
    //通过迭代器访问容器中的数据
    vector<int>::iterator itBegin = v.begin();  // begin是起始迭代器,指向容器中的第一个元素
    vector<int>::iterator itEnd = v.end();      // end是结束迭代器,指向容器中最后一个元素的下一个位置
    //第一种遍历方式
    while (itBegin != itEnd)
    {
        cout << *itBegin << endl;
        itBegin++;
    }
    //第二种遍历方式  
    for (itBegin; itBegin != itEnd; itBegin++)  // => for(vector<int>::iterator it = v.begin(); it != it.end(); it++)
    {
        cout << *itBegin << endl;
    }
    //第三种遍历方式
    for_each(v.begin(), v.end(), MyPrint);
}

int main()
{
    test01();
}
vector存放自定义数据类型
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

class Person
{
public:
    string m_Name;
    int m_Age;
    Person(string name, int age)
    {
        this->m_Age = age;
        this->m_Name = name;
    }
};

void printPerson(string name, int age)
{
   cout << "name: " << name << "\tage: " << age << endl; 
}
void test01()
{
    vector<Person> v;
    Person p1("Tom", 18);
    Person p2("Andy", 19);
    v.push_back(p1);
    v.push_back(p2);

    for (vector<Person>::iterator it = v.begin(); it != v.end(); it++)
    {
    //     cout << "name:" << it->m_Name << "\tage:" << it->m_Age << endl;
        cout << "name: " << (*it).m_Name << "\tage:" << (*it).m_Age << endl;
    }

    vector<Person>::iterator itBegin = v.begin();
    vector<Person>::iterator itEnd = v.end();
    while (itBegin != itEnd)
    {
        cout << "name:" << itBegin->m_Name << "\tage:" << itBegin->m_Age << endl;
        itBegin++;
    }
}
//放对象指针
void test02()
{
    vector<Person*> v;
    Person p1("Tom", 18);
    Person p2("Andy", 19);
    v.push_back(&p1);
    v.push_back(&p2);

    for (vector<Person*>::iterator it = v.begin(); it != v.end(); it++)
    {
        cout << "name:" << (*it)->m_Name << "\tage:" << (*it)->m_Age << endl;
    }
}

int main()
{
    // test01();
    test02();
}
vector容器中嵌套容器
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

void test01()
{
    vector< vector<int> > v;
    //创建小容器
    vector<int> v1; 
    vector<int> v2; 
    //向小容器中添加数据
    v1.push_back(10);
    v1.push_back(100);
    v1.push_back(1000);
    v2.push_back(20);
    v2.push_back(200);
    //向大容器中添加小容器
    v.push_back(v1);
    v.push_back(v2);
    //遍历
    for (vector<vector<int>>::iterator it = v.begin(); it != v.end(); it++)
    {
        for (vector<int>::iterator vit = (*it).begin(); vit != (*it).end(); vit++)
        {
            cout << *vit << " ";
        } 
        cout << endl;
    } 
}

int main()
{
    test01();
}

STL-常用容器

string容器

string基本概念

本质:string是C++风格的字符串,string本质上是一个类
stringchar* 区别:

  • string 是一个类,类内部封装了char*,管理这个字符串的是一个char*型的容器
  • char* 是一个指针

特点
string内部封装了很多成员方法,如:find、copy、delete、insert、replace等。
string管理char*所分配的内存,不用担心越界问题,由类内部负责。

string构造函数

构造函数原型:

  • string(); //创建空字符串,例如:string str;
    string(const char* s); //使用字符串s初始化
  • string(const string& str); //使用一个string对象初始化另一个string对象
  • string(int n, char c); //使用n个字符c初始化
#include <iostream>
#include <string>

using namespace std;

void test01()
{
    // string(); 
    string s1;      //默认构造

    // string(const char* s);
    const char * str1 = "hello world";
    string s2(str1); 

    // string(const string& str);       //拷贝构造
    const string& str2 = "hello cpp";
    string s3(str2);    // => string s3(s2);

    // string(int n, char c);
    int n=5;
    char c = 'c';
    string s4(n, c);

    cout << "s1: " << s1 << "\ts2: " << s2 << "\ts3: " << s3 << "\ts4: " << s4 << endl;
}

int main()
{
    test01();
}

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赋值给当前的字符串
#include <iostream>
#include <string>
using namespace std;

void test01()
{
    string str1="hello world";
    cout << "str1= " << str1 << endl;

    string str2 = str1;
    cout << "str2= " << str2 << endl;

    string str3;
    str3 = 'p';
    cout << "str3= " << str3 << endl;

    string str4;
    str4.assign("hello cpp");
    cout << "str4= " << str4 << endl;

    string str5;
    str5.assign("dkjalf", 3);
    cout << "str5= " << str5 << endl;

    string str6;
    str6.assign(str5);
    cout << "str6= " << str6 << endl;

    string str7;
    str7.assign(5,'p');
    cout << "str7= " << str7 << endl;
}

int main()
{
    test01();
}

string字符串拼接

函数原型:

  • string& operator+=(const char* str); //重载+=操作符
  • string& operator+=(const char c); //重载+=操作符
  • string& operator+=(const string& s); //重载+=操作符
  • string& append(const char *s); //把字符串s连接到当前字符串的结尾
  • string& append(const char *s, int n); //把字符串s的前n个字符连接到当前字符串的结尾
  • string& append(const string &s); //同operator+=(const string& s)
  • string& append(const string &s, int pos, int n); //把字符串s从pos位置开始的n个字符连接到当前字符串的结尾
#include <iostream>
#include <string>
using namespace std;

void test01()
{
    string str1="hello";
    str1 += " world";
    cout << "str1= " << str1 << endl;

    string str2="I hate you";
    str2 += '!';
    cout << "str2= " << str2 << endl;

    string str3="say:";
    str3 += str1;
    cout << "str3= " << str3 << endl;

    string str4="我";
    str4.append("爱玩");
    cout << "str4= " << str4 << endl;

    string str5="cpp";
    str5.append(" and python", 7);
    cout << "str5= " << str5 << endl;

    string str6="He loves ";
    str6.append(str5);
    cout << "str6= " << str6 << endl;

    string str7="hello cpp, ";
    str7.append("heiwoejos", 2, 3);
    cout << "str7= " << str7 << endl;
}

int main()
{
    test01();
}

string 字符串查找和替换

函数原型:

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

Tips:find 从左往右查找,rfind 从右往左查找

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

int main()
{
    string str1="hello world";
    cout << "l 第一次出现的位置: " << str1.find("l") << endl;
    cout << "l 最后一次出现的位置: " << str1.rfind("l") << endl;

    str1.replace(6, 5, "cpp");
    cout << "替换之后的str1: " << str1 << endl;
}

string字符串比较

比较方法:字符串比较是按字符的ASCII码进行对比(= 返回0;> 返回1;< 返回-1)
函数原型:

  • int compare(const string &s) const; //与字符串s比较
  • int compare(const char *s) const; //与字符串s比较
#include <iostream>
#include <string>
using namespace std;
int main()
{
	string str1="hello", str2="hellp";
    int ret = str1.compare(str2);
    cout << ret << endl;
    if (ret < 0)
    {
        cout << "str1 < str2" << endl;
    }
    else if (ret > 0)
    {
        cout << "str1 > str2" << endl;
    }
    else
    {
        cout << "str1 == str2" << endl;
    } 
}

string字符串存取

string中单个字符存取方式:

  • char& operator[](int n); //通过[]取字符
  • char& at(int n); //通过at取字符
#include <iostream>
#include <string>
using namespace std;

int main()
{
	string str = "hello";
    cout << "左->右,第1个字符: " << str[1] << endl;
    cout << "左->右,第2个字符: " << str.at(2) << endl;
    //修改单个字符
    str[2] = 'p';
    cout << "str:" << str << endl;
	str.at(2) = 'q';
	cout << "str:" << str << endl;
}

string字符串插入、删除

函数原型:

  • string& insert(int pos, const char* s); //插入字符
  • string& insert(int pos, const string& str); //插入字符
  • string& insert(int pos, int n, char c); //在pos位置插入n个字符c
  • string& erase(int pos, int n=npos); //删除从pos开始的n个字符
#include <iostream>
#include <string>
using namespace std;

int main()
{
	string str="hello", ss="99";
    str.insert(1, "88");
    cout << "1. str: " << str << endl;
    str.insert(2, ss);
    cout << "2. str: " << str << endl;
    str.insert(3, 5, 'c');
    cout << "3. str: " << str << endl;
    str.erase(2, 5);
    cout << "4. str: " << str << endl;
}

string字串获取

函数原型:

  • string substr(int pos=0, int n=npos) const; //返回从pos开始的n个字符组成的字符串
int main()
{
	string str="hello";
    string ss = str.substr(1, 2);
    cout << ss << endl;
}

vector容器

vector基本概念

vector数据结构和数组非常相似,也称为单端数组
数组是静态空间,vector可以动态扩展
动态扩展:不是原空间之后续接新空间,而是重新开辟更大的内存空间,然后将原数据拷贝至新空间,释放原空间。

vector构造函数

函数原型:

  • vector<T> v; //采用模板实现,默认构造函数
  • vector(v.begin(), v.end()); //将v[begin(),end())区间的元素拷贝给本身
  • vector(n, elem); //构造函数将n个elem拷贝给本身
  • vector(const vector &vec); //拷贝构造函数
#include <iostream>
#include <vector>
using namespace std;

void printVector(vector<int> &v)
{
    for (vector<int>::iterator it = v.begin(); it < v.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}

void test01()
{
    vector<int> v1;     //默认构造 无参构造
    for (int i = 0; i < 5; i++)
    {
        v1.push_back(i);
    }
    printVector(v1);
    //通过区间方式进行构造
    vector<int> v2(v1.begin(),v1.end());
    printVector(v2);
    //n个elem方式构造
    vector<int> v3(10, 5);
    printVector(v3);
    //拷贝构造
    vector<int> v4(v3);
    printVector(v4);
}

int main()
{
    test01();
}

vector赋值操作

函数原型:

  • vector& operator=(const vector &vec); //重载=操作符
  • assign(begin, end); //将[begin, end)区间的数据拷贝赋值给本身
  • assign(n, elem); //将n个elem拷贝赋值给本身
#include <iostream>
#include <vector>
using namespace std;

void printVector(vector<int> &v)
{
    for (vector<int>::iterator it = v.begin(); it < v.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}

void test02()
{
    vector<int> v1;
    for (int i = 0; i < 5; i++)
    {
        v1.push_back(i);
    }
    printVector(v1);
    //赋值  operator=
    vector<int> v2;
    v2 = v1;
    printVector(v2);
    //赋值  assign(begin, end)
    vector<int> v3;
    v3.assign(v1.begin(), v1.end());
    printVector(v3);
    //赋值  assign(n, elem)
    vector<int> v4;
    v4.assign(5,8);
    printVector(v4);
}
int main()
{
    test02();
}

vector容量和大小

函数原型:

  • empty(); //判断容器是否为空
  • capacity(); //容器的容量
  • size(); //返回容器中元素的个数
  • resize(int num); //重新指定容器的长度为num,若容器变长,则以默认值填充新位置;如果容器变短,则尾部超出容器长度的元素被删除。
  • resize(int num, elem); //重新指定容器的长度为num,若容器变长,则以elem的值填充新位置;如果容器变短,则尾部超出容器长度的元素被删除。
void printVector(vector<int> &v)
{
    for (vector<int>::iterator it = v.begin(); it < v.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}

void test01()
{
    vector<int> v1;
    for (int i = 0; i < 9; i++)
    {
        v1.push_back(i);
    }
    printVector(v1);
    //重新指定大小
    v1.resize(15);
    printVector(v1);
    
    if (v1.empty())
    {
        cout << "容器为空" << endl;
    }
    else
    {
        cout << "容器的容量: " << v1.capacity() << endl;
        cout << "容器的大小: " << v1.size() << endl;
    }
}
void test02()
{
	vector<int> v1;
    for (int i = 0; i < 9; i++)
    {
        v1.push_back(i);
    }
	//重新指定大小并且指定填充值
    v1.resize(16, 10);
    printVector(v1);
}
int main()
{
	test01();
	test02();
}

vector插入和删除

函数原型:

  • push_back(elem); //尾部插入元素elem
  • pop_back(); //尾部删除元素
  • insert(const_iterator pos, elem); //迭代器指向位置pos(处)插入元素elem
  • insert(const_iterator pos, int count, elem); //迭代器指向位置pos(处)插入count个元素elem
  • erase(const_iterator pos); //删除迭代器指向的元素
  • erase(const_iterator start, const_iterator end); //删除迭代器从start到end之间的元素
  • clear(); //删除容器中所有元素
#include <iostream>
#include <vector>
using namespace std;

void printVector(vector<int> &v)
{
    for (vector<int>::iterator it = v.begin(); it < v.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}

int main()
{
    vector<int> v1;
    v1.push_back(3);
    v1.push_back(4);
    v1.push_back(5);
    printVector(v1);
    //尾删
    v1.pop_back(); 
    printVector(v1);
    //插入  [头部插入一个值]
    v1.insert(v1.begin(), 8);
    printVector(v1);
    //插入  [头部插入两个值]
    v1.insert(v1.begin(), 2, 9);
    printVector(v1);
    //头删  [删除一个元素]
    v1.erase(v1.begin());
    printVector(v1);
    //头删  [删除多个元素]
    v1.erase(v1.begin(), v1.end());    // ==> v1.clear()
    printVector(v1);
    //清空
    v1.push_back(1);
    printVector(v1);
    v1.clear();
    printVector(v1);
}

vector数据存取

函数原型:

  • at(int idx); //返回索引idx所指的数据
  • operator[]; //返回索引idx所指的数据
  • front(); //返回容器中第一个数据元素
  • back(); //返回容器中最后一个数据元素
#include <iostream>
#include <vector>
using namespace std;

void printVector(vector<int> &v)
{
    for (vector<int>::iterator it = v.begin(); it < v.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}

int main()
{
    vector<int> v1;     //默认构造 无参构造
    for (int i = 0; i < 5; i++)
    {
        v1.push_back(i);
    }
    //利用[]访问容器中的元素
    for (int i = 0; i < v1.size(); i++)
    {
        cout << "v1[]: " << v1[i] << endl;
    }
    //利用at访问容器中的元素
    for (int i = 0; i < v1.size(); i++)
    {
        cout << "v1.at(): " << v1.at(i) << endl;
    }
    //利用front()访问容器中的第一个元素
    cout << "the first of elem for v1 is: " << v1.front() << endl;
    //利用back()访问容器中的最后一个元素
    cout << "the last of elem for v1 is: " << v1.back() << endl;
}

vector互换容器

功能描述:实现两个容器内元素进行互换
函数原型:swap(vec); //将vec与本身的元素互换

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

void printVector(vector<int> &v)
{
    for (vector<int>::iterator it = v.begin(); it < v.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}

int main()
{
	vector<int> v1;
    v1.push_back(5);
    v1.push_back(6);
    // printVector(v1);
    vector<int> v2;
    v2.push_back(7);
    v2.push_back(8);
    // printVector(v2);

    v2.swap(v1);
    printVector(v1);
    printVector(v2);
}

vector预留空间

功能描述:减少vector在动态扩展容量时的扩展次数
函数原型:reserve(int len); //容器预留len个元素长度,预留位置不初始化,元素不可访问。

#include <iostream>
#include <vector>
using namespace std;
int main()
{
	vector<int> v;
    v.reserve(100);
}

deque容器

deque基本概念

功能:双端数组,头尾皆可插入、删除
deque与vector的区别:

  • vector对于头部的插入、删除操作,效率低;且数据越大效率越低
  • deque对头部的插入、删除操作比vector快
  • vector访问元素比deque快,这和两者的内部实现有关系

deque内部工作原理
deque内部有个中控器,维护每段缓冲区的内容,缓冲区中存放真实数据,中控器维护的是每段缓冲区的地址,使得使用deque时像一片连续的内存空间。

deque容器的迭代器也支持随机访问。

deque构造函数

函数原型:

  • deque<T> deqT; //默认构造形式
  • deque(begin, end); //构造函数将[begin, end)区间的元素拷贝给本身
  • deque(n, elem); //构造函数将n个elem元素拷贝给本身
  • deque(const deque &deq); //拷贝构造函数
#include <iostream>
#include <deque>
using namespace std;

void printDeque(deque<int> &d)
{
    // for (deque<int>::iterator it = d.begin(); it != d.end(); it++)
    for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}

void test01()
{
    deque<int> d1;
    for (int i = 0; i < 10; i++)
    {
        d1.push_back(i);
    }
    printDeque(d1);

    deque<int> d2(d1.begin(), d1.end());
    printDeque(d2);

    deque<int> d3(10, 8);
    printDeque(d3);

    deque<int> d4(d3);
    printDeque(d4);
}

int main()
{
    test01();
}

deque赋值操作

函数原型:

  • deque& operator=(const deque &deq); //重载=操作符 [deque2 = deque1;]
  • assign(begin, end); //将[begin, end)区间的数据拷贝赋值给本身 [d1.assign(d2.begin(), d2.end());]
  • assign(n, elem); //将n个elem赋值给本身 [d2.assign(8, 10);]

deque大小操作

函数原型:

  • deque.empty(); //判断deque是否为空
  • deque.size(); //返回deque容器中元素的个数
  • deque.resize(num); //重新指定容器的长度为num,若容器变长,则以默认值填充新位置;如果容器变短,则尾部超出容器长度的元素被删除。
  • deque.resize(num, elem); //重新指定容器的长度为num,若容器变长,则以elem的值填充新位置;如果容器变短,则尾部超出容器长度的元素被删除。

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, begin, end); //在pos位置插入[begin, end)区间的数据,无返回值
  • clear(); //清空容器中的所有数据
  • erase(begin, end); //删除[begin,end)区间的数据,返回下一个数据的位置。
  • erase(pos); //删除pos位置的数据,返回下一个数据的位置。

deque数据存取

函数原型:

  • at(int idx); //返回索引idx所指的数据
  • operator[]; //返回索引idx所指的数据
  • front(); //返回容器中第一个数据元素
  • back(); //返回容器中最后一个数据元素

deque排序

功能描述:利用算法实现对deque容器进行排序
算法:sort(iterator begin, iterator end); //对begin和end区间内的元素进行排序。

#include <iostream>
#include <deque>
#include <algorithm>

using namespace std;

void printDeque(deque<int> &deq)
{
	for (deque<int>::const_iterator it = deq.begin();  it != deq.end();  it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

void test01()
{
	deque<int> d1;
	d1.push_back(10);
	d1.push_back(8);
	d1.push_back(15);
	d1.push_back(30);
	d1.push_back(9);
	sort(d1.begin(), d1.end());
	printDeque(d1);
}

int main()
{
	test01();
}

stack容器

stack基本概念

概念:stack是一种先进后出(FILO)的数据结构,它只有一个出口。“
只有栈顶元素可以被外界使用,所以栈不能被遍历。
添加元素叫
入栈
push
删除元素叫出栈pop

stack常用接口

构造函数

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

赋值操作stack &operator=(const stack &stk); //重载等号操作符
数据存取

  • push(elem); // 向栈顶添加元素
  • pop(); //从栈顶移除第一个元素
  • top(); //返回栈顶元素

大小操作

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

queue容器

queue基本概念

概念:queue是一种**先进先出(FIFO)**的数据结构,它有两个出口。
队列中只有队头和队尾可以被外界使用,所以队列不能被遍历。
队列中进数据称为:入队 push
队列中出数据称为:出队 pop

queue常用接口

构造函数

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

赋值操作queue &operator=(const queue &que); //重载等号操作符
数据存取

  • push(elem); //从队尾添加元素
  • pop(); //从队头移除第一个元素
  • back(); //返回最后一个元素
  • front(); //返回第一个元素
    大小操作
  • empty(); //判断队列是否为空
  • size(); //返回队列的大小

list容器

list基本概念

功能:将数据进行链式存储
链表(list)是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的

链表的组成:链表由一系列结点组成
结点的组成:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域

STL中的链表是一个双向循环链表
链表list中的迭代器只支持前移和后移,属于双向迭代器
链表list的优点:

  • 采用动态存储分配,不会造成内存浪费和溢出
  • 链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素

链表list的缺点:

  • 所占空间比数组大(需要存储数据域和指针域)
  • 遍历时比数组耗时

list有一个重要的性质:插入、删除操作都不会造成原有list迭代器的失效。这在vector里是不成立的。

list构造函数

功能:创建list容器
函数原型

  • list<T> lst; //list采用模板类实现对象的默认构造形式
  • list(begin, end); //构造函数将[begin,end)区间中的元素拷贝给本身
  • list(n, elem); // 构造函数将n个elem拷贝给本身
  • list(const list &lst); //拷贝构造函数
#include <iostream>
#include <list>

int main()
{
	list<int> lst;			//默认构造
	lst.push_back(10);
	lst.push_back(30);
	lst.push_back(20);
	printList(lst);

	list<int> lst1(lst.begin(), lst.end());	//区间构造	
	printList(lst1);

	list<int> lst2(lst);	//拷贝构造
	printList(lst2);

	list<int> lst3(10, 8);	//list(n,elem)
	printList(lst3);
}

list赋值和交换

功能:给list容器赋值、交换list容器
函数原型:

  • assign(beg, end); //将[beg,end)区间中的数据拷贝赋值给本身
  • assign(n, elem); //将n个elem拷贝赋值给本身
  • list &operator=(const list &lst); //重载等号操作符
  • swap(lst); //将lst与本身的元素互换

list大小操作

函数原型:

  • size(); //返回list中元素的个数
  • empty(); //判断list是否为空
  • resize(num); //重新指定list的长度为num,若list变长,则以默认值填充新位置;若list变短,则末尾超出list长度的元素被删除
  • resize(num, elem); //重新指定list的长度为num,若list变长,则以elem值填充新位置;若list变短,则末尾超出list长度的元素被删除

list插入和删除

函数原型:

  • push_back(elem); //在list尾部加入elem元素
  • pop_back(); //删除list中最后一个元素
  • push_front(elem); //从list开头插入elem元素
  • pop_front(); //从list开头移除第一个元素
  • insert(pos, elem); //在pos位置插入elem元素的拷贝,返回新数据的位置
  • insert(pos, n, elem); //在pos位置插入n个elem数据,无返回值
  • insert(pos,beg,end); //在pos位置插入[beg,end)区间的数据,无返回值
  • clear(); //清空list中的所有数据
  • erase(beg, end); //删除[beg,end)区间的数据,返回下一个数据的位置
  • erase(pos); //删除pos位置的数据,返回下一个数据的位置
  • remove(elem); //删除list中所有与elem值匹配的元素

list数据存取

函数原型:

  • front(); //返回第一个元素
  • back(); //返回最后一个元素

list反转和排序

功能描述:将list容器中的元素反转,以及将list容器中的数据进行排序
函数原型:

  • reverse(); //反转链表list
  • sort(); //链表list排序

set/multiset容器

set基本概念

所有元素都会在插入时自动被排序
本质:set/multiset属于关联式容器,底层结构是用二叉树实现。
set和multiset区别

  • set不允许容器中有重复的元素
  • multiset允许容器中有重复的元素

set构造和赋值

构造

  • set<T> st; //默认构造函数
  • set(const set &st); //拷贝构造函数

赋值

  • set &operator=(const set &st); //重载等号操作符

set大小和交换

函数原型:

  • size(); //返回set容器中元素的数目
  • empty(); //判断set容器是否为空
  • swap(st); //交换两个set容器

set插入和删除

函数原型:

  • insert(elem); //在set容器中插入elem数据
  • clear(); //清除所有元素
  • erase(pos); //删除pos位置的元素,返回下一个元素的迭代器
  • erase(beg, end); //删除区间[beg,end)的所有元素,返回下一个元素的迭代器
  • erase(elem); //删除set容器中值为elem的元素

set查找和统计

函数原型:

  • find(key); //查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end()
  • count(key); //统计key的元素个数 (对于set而言,结果为0或1)
#include <iostream>
#include <set>
using namespace std;
int main()
{
	set<int> s;
	s.insert(10);
	s.insert(20);
	s.insert(30);
	s.insert(40);
	s.insert(40);

	set<int>::iterator pos = s.find(20);
	if (pos != s.end())
	{
		cout << "Find it. Value is " << *pos << endl;
	}

	int num = s.count(40);
	cout << "count(40): " << num << endl;	//输出:1	对于set而言统计的结果只有0和1两种
}

set和multiset区别

  • set不可以插入重复的数据,而multiset可以
  • set插入数据的同时会返回插入结果,表示插入是否成功
  • multiset不会检测数据,因此可以插入重复数据
#include <iostream>
#include <set>
using namespace std;

void printSet(set<int> &s)
{
	for (set<int>::iterator it = s.begin(); it != s.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

void printMultiset(multiset<int>& ms)
{
	for (set<int>::iterator it = ms.begin(); it != ms.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}
int main()
{
	set<int> s;
	s.insert(10);
	pair<set<int>::iterator, bool> ret = s.insert(20);
	if (ret.second)
	{
		cout << "插入成功" << endl;
	}
	pair<set<int>::iterator, bool> ret2 = s.insert(20);
	if (ret2.second)
	{
		cout << "插入成功" << endl;
	}
	else
	{
		cout << "插入失败" << endl;
	}
	
	printSet(s);

	multiset<int> ms;
	ms.insert(10);
	ms.insert(10);
	ms.insert(20);
	printMultiset(ms);
}

pair对组创建

功能描述:成对出现的数据,利用对组可以返回两个数据
两种创建方式

  • pair<type, type> p(value1, value2);
  • pair<type, type> p = make_pair(value1, value2);

set容器排序

set容器默认排序规则为从小到大。
利用仿函数,可以改变set的排序规则

set存放内置数据类型 - 改变排序
#include <iostream>
#include <set>

//仿函数
class MyCompare
{
public:
	bool operator()(int v1, int v2) const
	{
		return v1 > v2;
	}
};
int main()
{
	set<int> s1;
	s1.insert(10);
	s1.insert(50);
	s1.insert(30);
	s1.insert(90);
	s1.insert(70);
	printSet(s1);

	set<int, MyCompare> s2;
	s2.insert(10);
	s2.insert(50);
	s2.insert(30);
	s2.insert(90);
	s2.insert(70);
	for (set<int, MyCompare>::iterator it = s2.begin(); it != s2.end(); it++)
	{
		std::cout << *it << " ";
	}
	std::cout << endl;
}
set存放自定义数据类型 - 改变排序
class Person
{
public:
	string m_Name;
	int m_Age;
	Person(string name, int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
	bool operator<(const Person p) const
	{
		return this->m_Age < p.m_Age;
	}
};

//仿函数
class MyCompare2
{
public:
	bool operator()(const Person &p1, const Person &p2) const
	{
		return p1.m_Age > p2.m_Age;
	}
};

int main()
{
	set<Person> sp;
	Person p1("Tom", 18);
	Person p2("Andy", 28);
	Person p3("Sunny", 22);
	sp.insert(p1);
	sp.insert(p2);
	sp.insert(p3);
	for (set<Person>::iterator it = sp.begin(); it != sp.end(); it++)
	{
		std::cout << (*it).m_Name << " " << it->m_Age << "\t";
	}
	std::cout << endl;

	set<Person, MyCompare2> sp2;
	sp2.insert(p1);
	sp2.insert(p2);
	sp2.insert(p3);
	for (set<Person, MyCompare2>::iterator it = sp2.begin(); it != sp2.end(); it++)
	{
		std::cout << (*it).m_Name << " " << (*it).m_Age << "\t";
	}
	std::cout << endl;
}

map/multimap容器

map基本概念

  • map中所有元素都是pair
  • pair中第一个元素为key(键值),起索引作用;第二个元素为value(实值)
  • 所有元素都会根据元素的键值自动排序

本质:map/multimap属于关联式容器,底层结构是用二叉树实现。
优点: 可以根据key值快速找到value值
map和multimap的区别:

  • map不允许容器中有重复key值元素
  • multimap允许容器中有重复key值元素

map构造和赋值

构造

  • map<T1, T2> mp; //map默认构造函数
  • map(const map &mp); //拷贝构造函数

赋值
map &operator=(const map &mp); //重载等号操作符

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

void printMap(map<int, int> &m)
{
	for (map<int, int>::iterator it = m.begin(); it != m.end(); it++)
	{
		cout << "key= " << (*it).first << " value= " << it->second << "\t";
	}
	cout << endl;
}

int main()
{
	map<int, int> m;
	m.insert(pair<int, int>(1, 20));
	m.insert(pair<int, int>(2, 40));
	m.insert(pair<int, int>(3, 80));
	printMap(m);
	
	map<int, int> m2(m);
	printMap(m2);
	
	map<int,int> m3;
	m3 = m2;
	printMap(m3);
}

map大小和交换

函数原型:

  • size(); //返回容器中元素的数目
  • empty(); //判断容器是否为空
  • swap(st); //交换两个map容器

map插入和删除

函数原型:
insert(elem); //在map容器中插入元素
clear(); //清除所有元素
erase(pos); //删除pos迭代器所指的元素,返回下一个元素的迭代器
erase(beg, end); //删除区间[beg,end)的所有元素,返回下一个元素的迭代器
erase(key); //删除map中值为key的元素

#include <iostream>
#include <map>
using namespace std;
void test04()
{
	map<int, int> m;
	m.insert(pair<int, int>(1, 20));
	m.insert(make_pair(2, 40));
	m.insert(map<int,int>::value_type(3, 80));
	m[4] = 50;
	printMap(m);
}

map查找和统计

函数原型:

  • find(key); //查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end();
  • count(key); //统计key的元素个数 【对于map,结果只有0或1;multimap可能大于1】
void test04()
{
	map<int, int> m;
	m.insert(pair<int, int>(1, 20));
	m.insert(make_pair(2, 40));
	m.insert(map<int,int>::value_type(3, 80));
	m[4] = 50;
	m[5] = 50;
	printMap(m);
	//查找
	map<int, int>::iterator pos = m.find(2);
	if (pos != m.end())
	{
		cout << "Find it. key= " << (*pos).first  << " value= " << (*pos).second << endl;
	}
	else
	{
		cout << "Not find." << endl;
	}
	//统计
	cout << m.count(4) << endl;	//结果只有0或1
}

map容器排序

map容器默认排序规则为:按照key值进行从小到大排序
利用仿函数改变排序规则 方法同set排序

STL-函数对象

函数对象

基本概念

  • 重载函数调用操作符的类,其对象常称为函数对象
  • 函数对象使用重载的()时,行为类似函数调用,也叫仿函数

本质:函数对象(仿函数)是一个类,不是一个函数

基本使用

特点

  • 函数对象在使用时,可以像普通函数那样调用,可以有参数,可以有返回值
  • 函数对象超出普通函数的概念,可以有自己的状态
  • 函数对象可以作为参数传递

谓词

概念

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

一元谓词和二元谓词

#include <iostream>
#include <vector>
#include <algorithm>
//一元谓词
struct GreaterFive
{
	bool operator()(int val)
	{
		return val > 5;
	}
}
//二元谓词
class MyCompare()
{
public:
	bool operator()(int a, int b)
	{
		return a > b;
	}
}
void test01()
{
	vector<int> v;
	for(int i=0; i<10;i++)
	{
		v.push_back(i);
	}
	vector<int>::iterator it = find_if(v.begin(),v.end(),GreaterFive());
	if(it == v.end())
	{
		cout << "Not find." << endl;
	}
	else
	{
		cout << "Find it. value: " << *it << endl;
	}
}
void test02()
{
	vector<int> v1;
	v1.push_back(10);
	v1.push_back(80);
	v1.push_back(30);
	//默认排序 从小到大
	sort(v1.begin(),v1.end());
	//仿函数改变排序 从大到小
	sort(v1.begin(),v1.end(), MyCompare());
}
int main()
{
	test01();
	test02();
}

内建函数对象(内建仿函数)

内建函数对象意义

分类:算数仿函数、关系仿函数、逻辑仿函数
用法

  • 内建仿函数所产生的对象,用法和一般函数完全相同
  • 使用内建函数对象,需要引入头文件#include <functional>

算数仿函数

功能描述:

  • 实现四则运算
  • 其中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>; //取反仿函数
#include <iostream>
#include <functional>
using namespace std;
int main()
{
	//取反仿函数	一元仿函数
	negate<int> n;
	cout << n(50) << endl;	// -50
	//加法仿函数
	plus<int> p;
	cout << p(10, 5) << endl;	//15

	minus<int> mi;
	cout << mi(20, 10) << endl;	//10

	multiplies<int> mu;
	cout << mu(10, 10) << endl;	//100

	divides<int> d;
	cout << d(100, 10) << endl;	//10
	
	modulus<int> mod;
	cout << mod(12, 10) << endl;	//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>; //小于等于
#include <iostream>
#include <functional>
using namespace std;
void test01()
{
	equal_to<int> e;
	cout << e(2, 2) << endl;	//1

	not_equal_to<int> ne;
	cout << ne(1, 2) << endl;	//1

	greater<int> g;
	cout << g(3, 2) << endl;	//1

	greater_equal<int> ge;
	cout << ge(2, 2) << endl;	//1

	less<int> le;
	cout << le(2, 3) << endl;	//1

	less_equal<int> lq;
	cout << lq(2, 2) << endl;	//1
}
void test02()
{
	vector<int> v1;
	v1.push_back(10);
	v1.push_back(80);
	v1.push_back(30);
	//默认排序 从小到大
	sort(v1.begin(),v1.end());
	//内建仿函数改变排序 从大到小
	sort(v1.begin(),v1.end(), greater<int>());
}
int main()
{
	test01();
	test02();
}

逻辑仿函数

仿函数原型

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

STL-常用算法

概述

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

常用遍历算法

算法简介

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

for_each

功能描述:实现遍历容器
函数原型for_each(iterator begin, iterator end, _func);
//begin 开始迭代器
//end 结束迭代器
//_func 函数或者函数对象
Tips:==实际开发

#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;

int main()
{
	vector<int> v;
	v.push_back(10);
	v.push_back(30);
	v.push_back(20);
	v.push_back(60);
	//遍历算法 [普通函数]
	for_each(v.begin(), v.end(), print01);
	cout << endl;
	//遍历算法 [仿函数]
	for_each(v.begin(), v.end(), print02());
	cout << endl;
}

transform

功能描述:搬运容器到另一个容器中
函数原型transform(iterator beg1, iterator end1, iterator beg2, _func);
//beg1 源容器开始迭代器
//end1 源容器结束迭代器
//beg2 目标容器开始迭代器
//_func 函数或者函数对象
Tips: transform目标函数需要提前开辟空间,否则会报错

#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;

void test08()
{
	vector<int> v1;
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i + 1);
	}
	cout << " ==仿函数== " << endl;
	vector<int> v2;	//目标容器
	v2.resize(v1.size());	//目标容器需要提前开辟空间
	transform(v1.begin(), v1.end(), v2.begin(), transf2());
	for_each(v2.begin(), v2.end(), print02());
	cout << endl;
	cout << " ==普通函数== " << endl;
	vector<int> v3;	//目标容器
	v3.resize(v1.size());	//目标容器需要提前开辟空间
	transform(v1.begin(), v1.end(), v3.begin(), transf1);
	for_each(v3.begin(), v3.end(), print01);
}

常用查找算法

算法简介

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

find

功能描述:查找指定元素,找到返回指定元素的迭代器,找不到返回结束迭代器end()。
函数原型find(iterator beg, iterator end, value);
//beg 开始迭代器
//end 结束迭代器
//value 查找的元素

#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;

void test01()		//内置数据类型
{
	vector<int> v;
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}
	vector<string> v2;
	v2.push_back("ab");
	v2.push_back("ba");
	v2.push_back("cd");
	v2.push_back("ef");

	vector<string>::iterator pos = find(v2.begin(), v2.end(), "cd");
	if (pos != v2.end())
	{
		cout << "Find it, " << *pos << endl;
	}
	else
	{
		cout << "Not find." << endl;
	}
}

class Person
{
public:
	string m_Name;
	int m_Age;
	Person(string name, int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
	//重载==符号;让底层find知道如何对比person数据类型
	bool operator==(Person &p) const
	{
		if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
		{
			return true;
		}
		return false;
	}
};
void test02()	//自定义数据类型	
{
	vector<Person> v;
	Person p1("Tom", 18), p2("Andy", 20), p3("Sun", 17), p4("Tony", 28);
	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);

	Person p5("Cat", 5);

	vector<Person>::iterator it = find(v.begin(), v.end(), p3);
	if (it != v.end())
	{
		cout << "Find it. name: " << it->m_Name << ", age : " << it->m_Age << endl;
	}
	else
	{
		cout << "Not find." << endl;
	}
}

find_if

功能描述:按条件查找元素
函数原型find_if(iterator beg, iterator end, _Pred);
//按值查找元素,找到 返回指定位置迭代器,找不到 返回结束迭代器位置
//beg 开始迭代器
//end 结束迭代器
//_Pred 函数或者谓词(返回bool类型的仿函数)
Tips:查找到符合条件的第一个元素之后就停止查找,并返回迭代器。

#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;

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

class geraterPerson
{
public:
	bool operator()(Person &p)
	{
		return p.m_Age > 18;
	}
};

class Person
{
public:
	string m_Name;
	int m_Age;
	Person(string name, int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
	//重载==符号;让底层find知道如何对比person数据类型
	bool operator==(Person &p) const
	{
		if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
		{
			return true;
		}
		return false;
	}
};
void test01()
{
	//内置类型查找
	vector<int> v;
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i + 1);
	}
	vector<int>::iterator it = find_if(v.begin(), v.end(), geraterFive());
	if (it != v.end())
	{
		cout << *it << endl;
	}
	//自定义类型查找
	vector<Person> v2;
	Person p1("Tom", 18), p2("Andy", 20), p3("Sun", 17), p4("Tony", 28);
	v2.push_back(p1);
	v2.push_back(p2);
	v2.push_back(p3);
	v2.push_back(p4);
	vector<Person>::iterator it_p = find_if(v2.begin(), v2.end(), geraterPerson());
	if (it_p != v2.end())
	{
		cout << "name: " << it_p->m_Name << " age: " << it_p->m_Age << endl;
	}
}

abjacent_find

功能描述:查找相邻重复元素
函数原型adjacent_find(iterator beg, iterator end);
//查找相邻重复元素,返回相邻元素的第一个位置的迭代器
//beg 开始迭代器
//end 结束迭代器

binary_search(二分查找)

功能描述:查找指定元素是否存在
函数原型bool binary_search(iterator beg, iterator end, value); 只用于有序序列
//查找指定的元素,查到 返回true,否则返回false
//beg 开始迭代器
//end 结束迭代器
//value 查找的元素

count

功能描述:统计元素个数
函数原型count(iterator beg, iterator end, value);
//统计元素出现次数,返回值类型为int
//beg 开始迭代器
//end 结束迭代器
//value 要统计的元素
Tips:统计自定义数据类型时,需要配合重载operator==

count_if

函数原型count_if(iterator beg, iterator end, _Pred);
//按条件统计元素出现次数
//beg 开始迭代器
//end 结束迭代器
//_Pred 谓词

#include <iostream>
#include <algorithm>

using namespace std;

//谓词
class greaterFive
{
public:
	bool operator()(int val)
	{
		return val >= 5;
	}
};
void test01()
{
	//内置数据类型
	vector<int> v;
	v.push_back(2);
	v.push_back(5);
	v.push_back(1);
	v.push_back(3);
	v.push_back(8);
	v.push_back(5);
	int num = count_if(v.begin(), v.end(), greaterFive());
	cout << num << endl;
	//自定义数据类型  待补充。。。
}

常用排序算法

算法简介

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

sort

功能描述:对容器内元素进行排序
函数原型sort(iterator beg, iterator end, _Pred);
sort(iterator beg, iterator end); //默认是升序
利用谓词“_Pred”改变排序规则

#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;

//普通函数
void print01(int val)
{
	cout << val << " ";
}
//降序 谓词	仿函数
class Descending
{
public:
	bool operator()(int val1, int val2)
	{
		return val1 > val2;
	}
};
void test02()
{
	vector<int> v = {2,5,1,3,8,5};
	sort(v.begin(), v.end());
	for_each(v.begin(), v.end(), print01);
	cout << endl;
	//sort(v.begin(), v.end(), Descending());		//自定义实现降序
	sort(v.begin(), v.end(), greater<int>());	//STL中函数实现降序
	for_each(v.begin(), v.end(), print01);
}

random_shuffle (C++11)

函数原型random_shuffle(iterator beg, iterator end); //指定范围内的元素随机调整次序
Tips:C++17中没有random_shuffle,用shuffle代替

#include <iostream>
#include <algorithm>
#include <random>
using namespace std;
int main()
{
	vector<int> v = {1,3,8,5,9,5};
	//创建随机数引擎	[C++17没有random_shuffle函数]
	random_device rd;
	mt19937 rng(rd());
	//使用shuffle打乱容器中的数据
	shuffle(v.begin(),v.end(),rng);
	for(vector<int>::iterator it = v.begin(); it!= v.end(); it++)
	{
		cout << *it << " ";
	}
	cout <<  endl;

merge

函数原型merge(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest);
//容器元素合并,并存储到另一容器中 【两个容器必须是有序的
//dest 目标容器开始迭代器

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

int main()
{
	vector<int> v2 = { 7,8,9,10 };
	vector<int> v1 = { 1,2,3,4,5 };
	vector<int> vv;
	vv.resize(v1.size() + v2.size());
	merge(v2.begin(), v2.end(), v1.begin(), v1.end(), vv.begin());
	for_each(vv.begin(), vv.end(), print01);
}

reverse

函数原型reverse(iterator beg, iterator end); //反转指定范围内的元素

#include <iostream>
#include <algroithm>
using namespace std;
int main()
{
	vector<int> v1 = {1,2,3,4,5,6};
	reverse(v1.begin(),v1.end());
	for(vector<int>::iterator it=v1.begin(); it!=v1.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

常用拷贝和替换算法

算法简介

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

copy

函数原型copy(iterator beg, iterator end, iterator dest);
Tips:提前给目标容器dest 指定大小

replace

函数原型replace(iterator beg, iterator end, oldvalue, newvalue);
//将区间内旧元素替换成新元素
// oldvalue - 旧元素;newvalue - 新元素

replace_if

函数原型replace(iterator beg, iterator end, _pred, newvalue);
//满足条件(_pred)的替换成指定元素
//_pred - 谓词;newvalue - 替换的新元素

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

int main()
{
	vector<int> v1 = { 1,2,3,4,5,6 };
	replace_if(v1.begin(), v1.end(), greaterFour(), 8);
	for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

swap

函数原型swap(container c1, container c2);
// c1、c2必须是同类型的容器

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

int main()
{
	vector<int> v2 = { 7,8,9,10 };
	vector<int> v1 = { 1,2,3,4,5,6 };
	std::swap(v1, v2);
	/*结果:
		v1: 7,8,9,10
		v2: 1,2,3,4,5,6
	*/
}

常用算数生成算法

算数生成算法属于小型算法,使用时包含的头文件为#include <numeric

accumulate

函数原型accumulate(iterator beg, iterator end, value);
//计算区间内 容器元素累计总和
//value 起始值

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

int main()
{
	vector<int> v1 = { 1,2,3,4,5,6 };
	int num = accumulate(v1.begin(), v1.end(), 0);
	cout << num << endl;	// 21

fill

函数原型fill(iterator beg, iterator end, value); //向容器中填充指定元素value

#include <iostream>
#include <numeric>
#include <math.h>
using namespace std;
int main()
{
	vector<int> v1;
	v1.resize(10);
	fill(v1.begin(), v1.end(), 8);
	/*结果:8 8 8 8 8 8 8 8 8 8 */
}

常用集合算法

set_intersection

函数原型set_intersection(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest); //求两个集合的交集 [两个集合必须是有序序列];返回dest的最后一个元素的迭代器地址
//dest 目标容器的开始迭代器

#include <iostream>
#include <numeric>
#include <math.h>
using namespace std;
void test01()
{
	vector<int> v1 = { 1,2,3,4,5,6 };
	vector<int> v2 = { 5,6,7,8,9,10 };
	vector<int> v3;
	//取两者较小者作为目标容器的大小
	v3.resize(min(v1.size(), v2.size()));
	//返回目标容器的最后一个元素的迭代器地址
	vector<int>::iterator itEnd = set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin());
	//for_each(v3.begin(), itEnd, print01);
	for(vector<int>::iterator it=v3.begin(); it!=itEnd; it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

set_union

函数原型set_union(iterator beg1,iterator end1,iterator beg2, iterator end2,iterator dest);
//求两个集合的并集 [两个集合必须是有序序列];返回dest的最后一个元素的迭代器地址
//dest 目标容器的开始迭代器

#include <iostream>
#include <numeric>
#include <math.h>
using namespace std;
int main()
{
	vector<int> v4;
	v4.resize(v1.size() + v2.size());
	vector<int>::iterator it_end = set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), v4.begin());
	for(vector<int>::iterator it=v4.begin(); it!=itEnd; it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

set_difference

函数原型set_difference(iterator beg1,iterator end1,iterator beg2,iterator end2, iterator dest); //求两个集合的差集 [两个集合必须是有序序列];返回dest的最后一个元素的迭代器地址

#include <iostream>
#include <algorithm>
#include <numeric>
#include <math.h>
using namespace std;

//普通函数
void print01(int val)
{
	cout << val << " ";
}

int main()
{
	vector<int> v1 = { 1,2,3,4,5,6 };
	vector<int> v2 = { 5,6,7,8,9,10 };
	vector<int> v5;
	v5.resize(max(v1.size(),v2.size()));	//取两者较大者作为dest的空间
	vector<int>::iterator it_e = set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), v5.begin());
	cout << "v1和v2的差集:" << endl;
	for_each(v5.begin(), it_e, print01);
	cout << endl;

	vector<int> v6;
	v6.resize(max(v1.size(), v2.size()));
	it_e = set_difference(v2.begin(), v2.end(), v1.begin(), v1.end(), v6.begin());
	cout << "v2和v1的差集:" << endl;
	for_each(v6.begin(), it_e, print01);
}
/*输出:
v1和v2的差集:
1 2 3 4
v2和v1的差集:
7 8 9 10
*/
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值