C++基础学习笔记(下)

C++基础语法学习笔记 使用#define预处理器。使用const关键字。类型 * 指针名初始化: 指针 = &某变量通过指针,访问被指向的变量可以读取myxx = *p;也可以写入*p = ...要十分谨慎通过指针写入是危险的,这是c++强大的原因,也是bug发源地可以在参数中使用指针形参是指针类型时,可以改变实参的值我们可以利用这个特性,间接地实现多个返回值交换连个变量int t = *a;*a = *b;*b = t;return b;return 0;可以返回指针类型。 阅读详情

C++基础学习笔记(下)

文章目录

一、模板

(一)函数模板

  • C++的另一种编程思想为泛型编程,主要利用模板技术。

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

1.函数模板语法

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

语法:使用关键字:template

template<typename T>
函数声明或定义
  • template:声明创建模板;

  • typename:表明其后面的符号是一种数据类型,可以用class代替;

T:通用的数据类型,名称可以替换,通常为大写字母;

示例:交换两个未知类型的数

//声明一个模板,告诉编译器后面的 T 是一个通用数据类型
template <typename T> 
void swapNum(T &a, T &b){
    T temp = a;
    a = b;
    b = temp;
    cout << "a:" << a << endl;
    cout << "b:" << b << endl;
}
void test(){
    int a = 10;
    int b = 20;
    //第1种:自动类型推导
    swapNum(a, b);
    //第2种:显式指定类型
    swapNum <int>(a, b);
}

2.函数模板注意事项

(1)自动类型推导时,必须推导出一致的数据类型T才可以使用

(2)使用模板时必须确定出T的数据类型才可以使用。若不指定T数据类型则不能调用函数。

3.函数模板示例

封装一个选择排序算法从大到小对不同数据类型的数组进行排序。使用char数组和int数组测试。

template <class T>
void swapNums(T &a, T &b) {
    T temp = a;
    a = b;
    b = temp;
}

template <class T>
void printArr(T arr[], int len){
    for (int i = 0; i < len; ++i) {
        cout << arr[i] << " " << endl;
    }
}

template <class T>
void selectSort(T arr[], int len){
    for (int i = 0; i < len; ++i) {
        int max = i;//认定最大值的下标
        for (int j = i+1; j < len; ++j) {
            //若认定的最大值比遍历出的数值要小,说明j下标的元素才是最大值
            if (arr[max] < arr[j]){
                max = j;
            }
        }
        if (max != i){
            //交换max和i的元素
            swapNums(arr[max], arr[i]);
        }
    }
}

void sortTest(){
    //输入字符串数组
    char charArr[] = "agfcvd";
    int num = sizeof(charArr) / sizeof(char);
    selectSort(charArr, num);
    printArr(charArr, num);
    //输入int数组
    int arr[] = {12,4,1,0,6,3};
    int num1 = sizeof(arr) / sizeof(int);
    selectSort(arr, num1);
    printArr(arr, num1);
}

4.普通函数和函数模板的区别

(1)普通函数调用时可以自动进行类型转换(隐式类型转换),如将字符型变量变为ASCII码的int型数值;

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

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

(1)优先调用普通函数;

(2)可以通过空模板参数列表来强制调用函数模板;

(3)函数模板可以发生重载;

(4)若函数模板可以产生更好的匹配则优先调用函数模板。

void printNum(int a, int b){
    cout << "调用函数" << endl;
}

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

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

int main(){
    int a = 1, b = 2, c = 0;
    //(1)优先调用普通函数
    printNum(a, b);
    //(2)通过空模板参数列表强制调用模板函数
    printNum<>(a, b);
    //(3)调用重载模板函数
    printNum(a, b, c);
    //(4)虽然普通函数可以隐式类型转换,但匹配模板函数更好
    char d = 'd',e = 'e';
    printNum(d, e);
}

6.模板的局限性

对于某些特定数据类型需要用具体化方式做特殊实现。示例:比较两个Person类

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

template <class T>
bool compare(T &a, T &b){
    if(a == b){
        return true;
    } else{
        return false;
    }
}
//第1种:可以采用运算符重载
//第2种:利用具体化Person的版本实现
template <> bool compare(Person &a, Person &b){
    if(a.m_name == b.m_name && a.m_age == b.m_age){
        return true;
    } else{
        return false;
    }
}

void test(){
    Person p1("小a", 10);
    Person p2("小b", 20);
    int result = compare(p1, p2);
    if (result == true){
        cout << "相等!" << endl;
    } else{
        cout << "不等!" << endl;
    }
}

(二)类模板

1.类模板作用和语法

(1)作用:建立一个通用类,类中的成员数据类型可以不具体指定,用一个虚拟类型来代表。

(2)语法:

template <typename T>
类

2.类模板和函数模板区别

(1)类模板没有自动类型推导方式;

(2)类模板在模板参数列表中可以有默认参数。

template <class NameType, class AgeType = int>//类模板中,可以指定默认类型参数
class Person1{
public:
    Person1(NameType name, AgeType age){
        this->mName = name;
        this->mAge = age;
    }
    void showInfo(){
        cout << this->mName << endl;
        cout << this->mAge << endl;
    }
    NameType mName;
    AgeType mAge;
};

int main(){
    //此行错误!不能使用自动类型推导
//    Person p0("wzh", 10);
    //应该显式指定类型
    Person1 <string> p1("wzh", 10);
    p1.showInfo();
}

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

类模板和普通类中的成员函数创建时机不同:普通类的成员函数一开始可以创建,类模板中的成员函数在调用时才创建。

class Person1{
public:
    void showInfo1(){
        cout << "show person1" << endl;
    }
};
class Person2{
public:
    void showInfo2(){
        cout << "show person2" << endl;
    }
};

template <class T>
class MyClass{
public:
    T obj;
    //类模板中的成员函数
    void func1(){
        obj.showInfo1();
    }
    void func2(){
        obj.showInfo2();
    }
};

int main(){
    MyClass <Person1> m;
    m.func1();
//    m.func2();此步报错,函数调用时才会创建成员函数
}

4.类模板对象作为函数参数

类模板实例化出的对象向函数传参公有三种传入方式:(主要用第一种)

(1)指定传入的类型:直接显示对象的数据类型;

(2)参数模板化:将对象中的参数变为模板进行传递;

(3)整个类模板化:将这个对象类型模板化进行传递。

template <class T1, class T2>
class Person{
public:
    Person(T1 name, T2 age){
        this->mName = name;
        this->mAge = age;
    }
    void showInfo(){
        cout << "姓名:" << this->mName << "年龄:" << this->mAge << endl;
    }
    T1 mName;
    T2 mAge;
};
//第1种:指定传入的类型
void printPerson1(Person<string, int>&p){
    p.showInfo();
}

//第2种:参数模板化
template <class T1, class T2>
void printPerson2(Person<T1, T2>&p){
    p.showInfo();
}

//第3种:整个类模板化
template <class T>
void printPerson3(T &p){
    p.showInfo();
}

int main(){
    Person<string, int>p("w", 20);
    printPerson1(p);
    printPerson2(p);
    printPerson3(p);
}

5.类模板和继承

​ 当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型;若不指定,编译器无法给子类分配内存;若想灵活指定出父类中T的类型,子类也需变为类模板。

template <class T>
class Base{
    T m;
};
class Son1:public Base<int>{//1.此处须指定父类的T数据类型

};
//2.想灵活指定父类中的T类型,子类须变为类模板
template <class T1, class T2>
class Son2:public Base<T2>{
public:
    Son2(){
        cout << "T1类型:" << typeid(T1).name() << endl;//输出 int
        cout << "T2类型:" << typeid(T2).name() << endl;//输出 char
    }
};
int main(){
    Son2<int, char>s2;
}

6.类模板成员函数类外实现

需要加上模板参数列表:

template <class T1, class T2>
class Person{
public:
    Person(T1 name, T2 age);//构造函数
    void showInfo();//成员函数
    T1 mName;
    T2 mAge;
};

//构造函数 类外实现
template <class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age){
    this->mName = name;
    this->mAge = age;
}
//构造函数 类外实现
template <class T1, class T2>
void Person<T1, T2>:: showInfo(){
    cout << "名字:" << this->mName << endl;
    cout << "年龄:" << this->mAge << endl;
}

int main(){
    Person<string, int> p("wzh", 18);
    p.showInfo();
}

7.类模板份文件编写

​ 由于类模板中成员函数创建时机是在调用阶段,导致分文件(.cpp .h)编写时会链接不到,因此有两种解决方式:(1)直接包含.cpp源文件(不推荐);(2)将声明和实现写到同一个文件中,后缀改为.hpp(约定的后缀)。

8.类模板和友元

  • 全局函数类内实现:直接在类内声明友元即可;

  • 全局函数类外实现:需要提前让编译器知道全局函数的存在。

//1.通过全局函数打印信息
template <class T1, class T2>
class Person{
    //1.全局函数 类内实现
    friend void printInfo(Person<T1, T2>p){
        cout << "名字:" << p.mName << endl;
        cout << "年龄:" << p.mAge << endl;
    }

public:
    Person(T1 name, T2 age){
        this->mName = name;
        this->mAge = age;
    }
private:
    T1 mName;
    T2 mAge;
};

int main(){
    //1.类内调用
    Person<string, int>p1("tom", 22);
    printInfo(p1);
}
//类外实现,需要提前让编译器知道Person类存在
template <class T1,class T2>
class Person;

//通过全局函数打印信息
template <class T1,class T2>
void printInfo(Person<T1, T2>p){
    cout << p.mName << endl;
    cout << p.mAge << endl;
}


template <class T1, class T2>
class Person{
    //2.全局函数 类外实现,要加空模板参数列表
    //全局函数类外实现,需要让编译器提前知道这个函数的存在
    friend void printInfo<>(Person<T1, T2>p);
public:
    Person(T1 name, T2 age){
        this->mName = name;
        this->mAge = age;
    }
private:
    T1 mName;
    T2 mAge;
};

int main(){
    //类外实现
    Person<string, int>p("tom", 22);
    printInfo(p);
}

9.类模板案例

(1)目的:实现一个通用的数组类:

​ 可以对内置数据类型以及自定义数据类型的数据进行传输;

​ 将数组中的数据存储到堆区;

​ 构造函数中可以传入数组的容量;

​ 提供对应的拷贝构造函数以及operator=防止浅拷贝问题;

​ 提供尾插法和尾删法对数组中的数据进行增加和删除;

​ 可以通过下标的方式访问数组中的元素;

​ 可以获取数组中当前元素个数和数组容量。

(2)思路:

​ 首先,类中有T * pAddr,返回新开辟的T类型数据的指针,维护数组中的数据;有容量mCapa,有mSize。

​ 其次,提供构造函数接收容量;提供拷贝构造函数和operator=实现深拷贝防止浅拷贝;利用下标访问数组中元素的方法等。

(2)实现:

//这是MyArr.hpp文件
template <class T>
class MyArr{
public:
    MyArr(int capa){
//        cout << "有参构造函数调用" << endl;
        this->mCapa = capa;
        this->mSize = 0;
        this->pAddr = new T[this->mCapa];//开辟堆区空间
    }

    //拷贝构造
    MyArr(const MyArr& arr){
//        cout << "拷贝构造函数调用" << endl;
        this->mCapa = arr.mCapa;
        this->mSize = arr.mSize;
        this->pAddr = arr.pAddr;//会产生浅拷贝
        //改为以下,深拷贝
        this->pAddr = new T[arr.mCapa];
        //拷贝arr中的数据
        for (int i = 0; i < this->mSize; ++i) {
            this->pAddr[i] = arr.pAddr[i];
        }
    }

    //operator=防止浅拷贝问题
    MyArr&operator=(const MyArr& arr){
//        cout << "operator调用" << endl;
        //先判断原来堆区是否有数据,有则释放
        if (this->pAddr != NULL){
            delete [] this->pAddr;
            this->pAddr = NULL;
            this->mCapa = 0;
            this->mSize = 0;
        }
        this->mCapa = arr.mCapa;
        this->mSize = arr.mSize;
        this->pAddr = new T[arr.mCapa];
        for (int i = 0; i < mSize; ++i) {
            this->pAddr[i] = arr.pAddr[i];
        }
        return *this;
    }
    //尾插法
    void pushBack(const T &val){
        //判断容量是否等于大小
        if (this->mCapa == this->mSize){
            return;
        }
        //插到数组中最后一个位置
        this->pAddr[this->mSize] = val;
        //更新数组的大小
        this->mSize ++;
    }

    //尾删法
    void popBack(){
        //让用户访问不到最后一个元素,逻辑删除
        if (this->mSize == 0){
            return;
        }
        this->mSize --;
    }

    //因为是自定义的模板类,不能通过数组下标访问元素
    //下面实现通过下标访问数组中的元素
    //函数调用要作为左值存在,要返回引用
    T& operator[](int index){
        return this->pAddr[index];
    }

    //返回数组容量
    int getCapa(){
        return this->mCapa;
    }
    //返回数组大小
    int getSize(){
        return this->mSize;
    }

    //析构函数
    ~MyArr(){
//        cout << "析构函数调用" << endl;
        if(this->pAddr != NULL){
            delete [] this->pAddr;
            this->pAddr = NULL;
        }
    }
private:
    T * pAddr;
    int mCapa;//数组容量
    int mSize;//传入数组大小
};
//这是主文件
#include "MyArr.hpp"
//自定义数据类型
class Person{
public:
    Person(){}
    Person(string name, int age){
        this->mName = name;
        this->mAge = age;
    }
    string mName;
    int mAge;
};

//打印int数组元素
void printIntArr(MyArr<int>& arr){
    for (int i = 0; i < arr.getSize(); ++i) {
        cout << arr[i] << endl;
    }
}
//打印自定义数据类型数组元素
void printPersonArr(MyArr<Person>& arr){
    for (int i = 0; i < arr.getSize(); ++i) {
        cout << arr[i].mName << endl;
        cout << arr[i].mAge << endl;
    }
}


int main(){
    MyArr<int>arr1(5);//测试1

    for (int i = 0; i < 5; ++i) {
        arr1.pushBack(i);//尾插法向数组添加数据
    }
    printIntArr(arr1);
    cout << "arr1容量" << arr1.getCapa() << endl;
    cout << "arr1大小" << arr1.getSize() << endl;

    MyArr<int>arr2(arr1);//测试2
    printIntArr(arr2);
    arr2.popBack();
    cout << "arr2容量" << arr2.getCapa() << endl;//输出 5
    cout << "arr2大小" << arr2.getSize() << endl;//输出 4

    MyArr<int>arr3(100);//测试3
    arr3 = arr1;//测试4

    //测试1、2、3、4行的输出:
    //有参构造函数调用
    //拷贝构造函数调用
    //有参构造函数调用
    //operator调用
    //析构函数调用
    //析构函数调用
    //析构函数调用

    //——————————测试自定义数据类型——————————
    MyArr<Person> arr(10);
    Person p1("小A", 10);
    Person p2("小B", 15);
    Person p3("小C", 13);
    Person p4("大D", 16);
    Person p5("大E", 35);
    Person p6("小A", 8);
    //将数据插入到数组中
    arr.pushBack(p1);
    arr.pushBack(p2);
    arr.pushBack(p3);
    arr.pushBack(p4);
    arr.pushBack(p5);
    arr.pushBack(p6);
    //打印数组
    printPersonArr(arr);
    cout << "arr容量" << arr.getCapa() << endl;//输出arr容量10
    cout << "arr大小" << arr.getSize() << endl;//输出arr大小6
		//——————————测试完成——————————
}

二、STL基础

STL:标准模板库,广义上分为容器、算法和迭代器,容器和算法之间通过迭代器连接,几乎所有的代码都采用多了模板类或模板函数。

(一)STL六大组件

(1)容器:各种数据结构,如vector、list、deque、set、map,用来存放数据;

(2)算法:各种常用的算法,如sort、find、copy、for_each等;

(3)迭代器:扮演了容器与算法之间的胶合剂;

(4)仿函数:行为类似函数,可作为算法的某种策略;

(5)适配器:一种用来修饰同期或者仿函数或迭代器借口的东西;

(6)空间配置器:负责空间的配置与管理。

(二)容器、算法、迭代器

(1)容器就是将运用最广泛的一些数据结构(数组、链表、树、栈、队列、集合、映射表)实现出来,这些容器分为序列式容器和关联式容器两种:

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

(2)算法分为:

  • 质变算法:指运算过程中会更改区间内的元素的内容,如拷贝、替换、删除;

  • 非质变算法:指运算过程中不会更改区间内的元素内容,如查找、计数、遍历、寻找极值等。

(3)迭代器提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露容器内部表示方式。每个容器有自己专属的迭代器,使用类似于指针。

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

1.容器:verctor

(1)三种方式遍历容器中的数据:

int main() {
    //创建一个vector容器数组
    vector<int> v;
    v.push_back(1);
    v.push_back(4);
    v.push_back(2);

    //第一种遍历方式:通过迭代器访问容器中的数据
    vector<int>::iterator itBegin = v.begin();//起始迭代器,指向容器中第一个元素
    vector<int>::iterator itEnd = v.end();//结束迭代器,指向容器中的最后一个元素的下一个位置
    while (itBegin != itEnd){
        cout << *itBegin <<endl;
        itBegin ++;
    }
    //第二种遍历方式:通过for循环
    for (vector<int>::iterator it = v.begin();it != v.end();it++){
        cout << *it << endl;
    }

    //第三种遍历方式:利用自带的for_each函数
    for_each(v.begin(),v.end(),printInfo);



    return 0;
}

2.vector存放自定义数据类型

class Person{
public:
    Person(string name, int age){
        this->mName = name;
        this->mAge = age;
    }
    string mName;
    int mAge;
};
//——————————vector存放自定义数据类型并遍历——————————
void myDataType(){
    vector<Person> v;
    Person p1("小明",12);
    Person p2("小花",16);
    Person p3("小蓝",13);
    v.push_back(p1);
    v.push_back(p2);
    v.push_back(p3);
    //遍历
    for (vector<Person>::iterator it = v.begin();it != v.end();it ++){
        cout << "姓名:" << (*it).mName << "  年龄:" << (*it).mAge << endl;//通过解引用方式
        cout << "姓名:" << it->mName << "  年龄:" << it->mAge << endl;//通过指针方式

    }
}
//——————————存放自定义数据类型指针并遍历——————————
void myDataTypePoint(){
    vector<Person*> v;
    Person p1("a",12);
    Person p2("b",16);
    Person p3("c",13);
    v.push_back(&p1);
    v.push_back(&p2);
    v.push_back(&p3);
    //遍历
    for (vector<Person*>::iterator it = v.begin();it != v.end();it ++){
        cout << "姓名:" << (**it).mName << "  年龄:" << (**it).mAge << endl;//通过解引用方式
        cout << "姓名:" << (*it)->mName << "  年龄:" << (*it)->mAge << endl;//通过指针方式
    }
}

3.vector嵌套vector

int main(){
    vector<vector<int>>v;
    //创建小容器
    vector <int>v1;
    vector <int>v2;
    vector <int>v3;
    vector <int>v4;
    //向小容器中插入数据
    for (int i = 0; i < 4; ++i) {
        v1.push_back(i+1);
        v2.push_back(i+2);
        v3.push_back(i+3);
        v4.push_back(i+4);
    }
    //将小容器插入到大容器中
    v.push_back(v1);
    v.push_back(v2);
    v.push_back(v3);
    v.push_back(v4);
    //通过大容器,遍历所有数据
    for (vector<vector<int>>::iterator it = v.begin();it != v.end();it++){
        //(*it)是容器vector<int>
        for(vector<int>::iterator vit = (*it).begin();vit != (*it).end();vit++){
            cout << *vit << " ";
        }
        cout << endl;
    }
}

三、STL常用容器

(一)string容器

1.string基本概念

​ string本质上是一个类,和char* 的区别是:char是一个指针,string是一个类,类内部封装了char管理这个字符串,是一个char类型的容器。string内部封装了很多成员方法,如find、copy、delete、replace、insert等。string管理char所分配的内存,不用担心复制越界和取值越界等,由类内部进行负责。str.size()返回字符串长度。

2.string构造函数

构造函数的原型:

  • string:创建一个空的字符串
  • string(const char* s):使用字符串s初始化
  • string(const string& str):使用一个string对象初始化另一个string对象
  • string(int n, char c):使用n个字符c初始化
//第1种
string s1;
//第2种
const char * str = "wzh";
string s2(str);
cout << "s2:" << s2 << endl;
//第3种
string s3(s2);
cout << "s3:" << s2 << endl;
//第4种
string s4(10, 'a');
cout << "s4:" << s4 << endl;//输出10个a

3.string赋值操作

给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赋给当前字符串

4.字符串拼接

在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 pos, int n); //字符串s中中pos开始的n个字符连接到字符串结尾

5.字符串查找和替换

函数原型:

  • 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) const;//查找str最后一次位置,从pos开始找
  • 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=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

6.字符串的比较

按照ASCII进行比较,=返回0,>返回1,<返回-1.

函数原型:

  • int compare(const string& s) const; //与字符串s比较
  • int compare(const char *s) const; //与字符串s比较

7.字符存取

string中单个字符存取:

  • char& operator[] (int n);//通过[]方式取字符
  • char& at(int n); //通过at方法获取字符

8.字符串插入和删除

函数原型:

  • string& insert(int pos, const char* s); //插入字符串
  • string& insert(int pos, const string& str); //插入字符串
  • string& insert(int pos, int n, char c); //在指定位置插入n个字符
  • string& erase(int pos, int n = npos); //删除从pos开始的n个字符

9.子串获取

函数原型:

​ string substr(int pos = 0, int n = npos) const; //返回由pos开始的n个字符组成的字符串

(二)vector容器

1.vector基本概念

​ vector数据结构和数组非常相似,也称为单端数组。与普通数组不同在于数组是静态空间,而vector可以动态扩展,即并不是在原空间之后续接新空间,而是找更大的内存空间,将原有数据拷贝到新空间,释放原空间,在插入数据过程中会不断开辟新空间,可以利用reserve()预留空间。vector容器的迭代器是支持随机访问的迭代器。

2.vector构造函数

  • vector<T> v;//采用模板实现类实现,默认构造函数
  • vector(v.begin(), v.end())//将v[begin(), end())区间(前闭后开)中的元素拷贝给自身
  • vector(n, elem);//构造函数将n个elem拷贝给自身
  • vector(const cector &vec)//拷贝构造函数
void printVector(vector<int>&v){
    for (vector<int>::iterator it = v.begin();it != v.end(); it++){
        cout << *it << endl;
    }
    cout << endl;
}

int main(){
    //1.默认构造:无参构造
    vector<int>v1;
    for (int i = 0; i < 10; ++i) {
        v1.push_back(i);
    }
    printVector(v1);

    //2.通过区间方式构造
    vector<int>v2(v1.begin(), v1.end());
    printVector(v2);

    //3.n个elem方式构造
    vector<int>v3(10, 100);
    printVector(v3);//输出10个100
    
    //4.拷贝构造
    vector<int>v4(v3);
    printVector(v4);
}

3.vector赋值操作

函数原型:

  • vector& operator=(const vector &vec);//重载等号操作符
  • assign(beg, end);//将[beg, end)区间中的数据拷贝赋值给自身
  • assign(n, elem);//将n个elem拷贝赋值给自身
vector<int>v1;
    for (int i = 0; i < 10; ++i) {
        v1.push_back(i+1);
    }
    printVector(v1);

    //1.operator=赋值
    vector<int>v2;
    v2 = v1;

    //2.assign赋值
    vector<int>v3;
    v3.assign(v1.begin(), v1.end());
    
    //3.assign进行n个elem赋值
    vector<int>v4;
    v4.assign(3,20);

4.vector容量和大小

函数原型:

  • empty();//判断容器是否为空
  • capacity();//容器的容量
  • size();//返回容器中元素的个数
  • resize(int num);//重新指定容器的长度为num,容器变长则以默认值0填充新位置,变短则删除超出部分
  • resize(int num, elem);//重新制定容器长度num,变长则以elem填充新位置,变短则删除超出部分

5.vector的插入和删除

函数原型:

  • push_back(ele);//尾部插入元素ele
  • pop_back();//删除最后一个元素
  • insert(const_iterator pos, ele);//迭代器指向位置pos插入元素ele
  • insert(const_iterator pos, intcount, ele);//迭代器指向位置pos插入count个元素ele
  • erase(const_iterator start, const_iterator end);//删除迭代器指向的元素
  • erase(const_iterator start, const_iterator end);//删除迭代器从start到end之间的元素
  • clear();//删除容器中所有元素

6.vector数据存取

函数原型:

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

7.vector互换容器

函数原型:swap(vec);//将vec与本身的元素互换

通过创建匿名对象可以用来收缩vector占用的空间:vector<int>(v).swap(v)

8.vector预留空间

通过预留空间减少vector动态扩展时的扩展次数。

函数原型:reserve(int n);//容器预留len个元素长度,预留位置不初始化,元素不可访问。

(三)deque容器

1.deque基本概念

​ deque是双端数组。vector对于头部的插入删除效率低,数据量越大效率越低。而deque对头部的插入删除速度快,但vector访问元素的速度快。

2.deque构造函数

函数原型:

  • deque<T>//默认构造形式
  • deque(beg, end);//拷贝函数将[beg, end)区间的元素拷贝给自身
  • deque(n ,elem);//构造函数将n个elem拷贝给自身
  • deque(const deque &deq);//拷贝构造函数
void printDeque(const deque<int>&d){//打印不需要修改值,加const限制只读
    for(deque<int>::const_iterator it = d.begin();it != d.end();it ++){
        cout << *it << " ";
    }
    cout << endl;
}

int main(){
  	//第一种
    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,3);
    printDeque(d3);
		//第四种
    deque<int>d4(d3);
    printDeque(d4);
}

3.deque赋值操作

  • deque& operatot=(const deque &deq);//重载等号操作符
  • assign(beg, end);//将[beg, end)区间中的数据拷贝赋值给自身
  • assign(n, elem);//将n个elem拷贝赋值给自身

4.deque大小操作

函数原型:

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

5.deque插入和删除

函数原型:

​ 两端插入操作:

  • push_back(elem);//尾部插入元素elem

  • push_front(elem);//头部插入元素elem

  • pop_back();//删除最后一个元素

  • pop_front();//删除第一个元素

    指定位置操作:

  • insert(pos, elem);//在pos位置插入一个elem元素的拷贝,返回新数据的位置

  • insert(pos, n, elem);//在pos位置插入n个元素elem,无返回值

  • insert(pos, beg, end);//在pos位置插入[begin, end)的数据,无返回值

  • clear();//删除容器中所有元素

  • erase(beg, end);//删除[beg, end)区间的数据,返回下一个数据的位置

  • erase(pos);//删除pos位置的数据,返回下一个数据的位置

6.deque数据存取

函数原型:

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

7.deque排序

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

排序默认升序排,对于支持随机访问的迭代的的容器,都可以用sort算法直接排序。

(四)stack容器

1.基本概念

stack:栈,是先进后出的容器,只有一个出口。只有顶端的元素可以使用,所以不允许有遍历行为。

2.常用接口

(1)构造函数:

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

(2)赋值操作:

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

(3)数据存取:

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

(4)大小操作:

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

(五)queue容器

1.基本概念

先进先出的队列,从一端新增,另一端移除元素。队列中只有队头和队尾可以被使用,因此不允许有遍历行为。

2.常用接口

(1)构造函数:

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

(2)赋值操作:

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

(3)数据存取:

  • push(elem);//向队尾添加元素
  • pop();//从队头移除第一个元素
  • back();//返回最后一个元素
  • front();//返回第一个元素

(4)大小操作:

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

(六)list容器

1.list基本概念

​ list(链表):将数据进行链式存储,由一系列结点组成,结点包括存储数据元素的数据域和存储下一个结点地址的指针域组成。STL中的链表是双向循环链表。

链表优缺点:

​ 可以快速插入和删除元素,并且删除和插入数据不会造成迭代器的失效。采用动态存储分配,不会内存浪费或溢出。但遍历速度没有数组快,且占用空间比数组大。

2.list构造函数

函数原型:

  • list<T> lst//默认构造形式
  • list(beg, end);//拷贝函数将[beg, end)区间的元素拷贝给自身
  • list(n ,elem);//构造函数将n个elem拷贝给自身
  • list(const list &lst);//拷贝构造函数
void printList(const list<int>&l){
    for(list<int>::const_iterator it = l.begin();it != l.end(); it ++){
        cout << *it << " ";
    }
    cout << endl;
}

int main(){
    //默认构造
    list<int>l1;
    l1.push_back(1);
    l1.push_back(3);
    l1.push_back(4);
    printList(l1);
    //区间方式构造
    list<int>l2(l1.begin(), l1.end());
    printList(l2);
    //拷贝构造
    list<int>l3(l2);
    printList(l3);
    //n个elem
    list<int>l4(10,20);
    printList(l4);
}

3.list赋值和交换

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

4.list大小操作

函数原型:

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

5.list插入和删除

函数原型:

​ 两端操作:

  • push_back(elem);//尾部插入元素elem

  • push_front(elem);//头部插入元素elem

  • pop_back();//删除最后一个元素

  • pop_front();//删除第一个元素

    指定位置操作:

  • insert(pos, elem);//在pos位置(迭代器)插入一个elem元素的拷贝,返回新数据的位置

  • insert(pos, n, elem);//在pos位置插入n个元素elem,无返回值

  • insert(pos, beg, end);//在pos位置插入[begin, end)的数据,无返回值

  • clear();//删除容器中所有元素

  • erase(beg, end);//删除[beg, end)区间的数据,返回下一个数据的位置

  • erase(pos);//删除pos位置的数据,返回下一个数据的位置

  • remove(elem);//移除容器中所有与elem值匹配的元素

6.list数据存取

函数原型:

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

7.list逆序和排序

  • reverse();//逆序链表
  • sort();//链表排序,所有不支持随机访问迭代器的容器,不能用标准算法,内部提供此sort算法

若降序排列,则:

bool compare(int v2, int v2){
	return v1 > v2;//降序排列,则让v1>v2
}
lst.sort(compare);

8.排序案例

class Persons{
public:
    Persons(string name, int age, int height){
        this->mName = name;
        this->mAge = age;
        this->mHeight = height;
    }
    string mName;
    int mAge;
    int mHeight;
};


void printPerson(list<Persons>&p){
    for(list<Persons>::iterator it = p.begin();it != p.end(); it ++){
        cout << "姓名:" << it->mName << " 年龄:" << it->mAge << " 身高:" << it->mHeight << endl;
    }
}

//指定排序规则,按年龄升序,年龄相同时按照身高降序排列
bool compare(Persons &p1, Persons &p2) {
    if (p1.mAge == p2.mAge) {
        return p1.mHeight > p2.mHeight;
    } else {
        return p1.mAge < p2.mAge;
    }
}

int main(){
    list<Persons>l;
    Persons p1("小花", 23, 171);
    Persons p2("小蓝", 12, 155);
    Persons p3("小紫", 12, 165);
    Persons p4("小绿", 34, 174);
    Persons p5("小黑", 12, 130);
    l.push_back(p1);
    l.push_back(p2);
    l.push_back(p3);
    l.push_back(p4);
    l.push_back(p5);

    //打印数据
    cout << "————————排序前:————————" << endl;
    printPerson(l);
    //按照年龄进行升序排列
    l.sort(compare);
    cout << "————————排序后:————————" << endl;
    printPerson(l);
}

(七)set/multiset容器

1.基本概念

所有元素在插入时自动排序,属于关联式容器,底层结构使用二叉树实现。

set和multiset区别:

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

2.set构造函数和赋值

构造:

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

赋值:

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

3.set大小和交换

函数原型:

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

4.set插入和删除

函数原型:

  • insert(elem);//在容器中插入元素
  • clear();//清空容器元素
  • erase(pos);//删除pos迭代器所指的元素,返回下一个元素的迭代器
  • erase(elem);//删除容器中值为elem的元素

5.set查找和统计

函数原型:

  • find(key);//查找key是否存在,存在返回该元素的迭代器,不存在返回set.end()
  • count(key);//统计key的元素个数:对set只能是0或1

6.set和multiset的区别

  • set不能插入重复数据,multiset可以

  • set插入数据的同时会返回插入结果,使用对组pair:

  •   pair<set<int>::iterator, bool> ret = st.insert(1);
      if(ret.second){
        cout << "插入成功!"; 
      }
    
  • multiset不会检测数据,可以重复插入数据

7.pair对组创建

成对出现的数据,利用对组可以返回两个数据。有两种创建方式:

  • pair<type, type> p (value1, value2);
  • pair<type, type> p = make_pair(value1, value2);
//第一种方式
    pair<string, int>p("wzh", 25);
    //第二种方式
    pair<string, int>p2 = make_pair("wzh", 24);
    cout << p.first << p.second;

8.set容器排序

​ set容器默认从小到大排序,利用仿函数可以改变排序规则。自带数据类型可以不指定排序规则,自定数据类型必须指定。

(1)自带数据类型:

//使用仿函数指定规则
class Compares{
public:
    bool operator()(int v1, int v2){
        return v1 > v2;
    }
};
int main(){
    set<int>s1;
    s1.insert(6);
    s1.insert(1);
    s1.insert(3);
    s1.insert(2);
    s1.insert(4);

    for(set<int>::iterator it = s1.begin();it != s1.end();it ++){
        cout << *it << " ";
    }
    
    //指定排序规则为降序
    set<int, Compares>s2;//指定仿函数
    s2.insert(6);
    s2.insert(1);
    s2.insert(3);
    s2.insert(2);
    s2.insert(4);

    for(set<int, Compares>::iterator it = s2.begin();it != s2.end();it ++){
        cout << *it << " ";
    }
}

(2)自定义数据类型:

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

class SetCompare{
public:
    bool operator()(const Person&p1, const Person&p2){
        return p1.mAge > p2.mAge;
    }
};

int main(){
    //————————————自定义数据类型————————————
    set<Person, SetCompare>s;
    Person p1("大B",20);
    Person p2("小C",12);
    Person p3("大A",25);
    Person p4("小D",11);
    //s.insert(p1);自定义数据类型不能按照默认升序排,需要自定仿函数排序规则
    s.insert(p1);
    s.insert(p2);
    s.insert(p3);
    s.insert(p4);
    //输出
    for(set<Person>::iterator it = s.begin();it != s.end();it ++){
        cout << "姓名:" << it->mName << " 年龄:" << it->mAge << endl;
    }
}

(八)map/multimap容器

1.基本概念

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

map/multimap属于关联式容器,底层结构使用二叉树实现。map不允许有重复值,multimap允许有重复值。

2.map构造和赋值

构造:

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

赋值:

  • map& operator=(const map &mp);//重载等号操作符
map<int, int>mp;
    mp.insert(pair<int ,int>(5, 14));
    mp.insert(pair<int ,int>(3, 10));
    mp.insert(pair<int ,int>(7, 8));
    
		mp.insert(make_pair(4, 20));
    for(map<int, int>::iterator it = mp.begin();it != mp.end();it ++){
        cout << "key:" << (*it).first << " value:" << it->second << endl;
    }

3.map大小和交换

函数原型:

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

4.map插入和删除

函数原型:

  • insert(elem);//在容器中插入元素
  • clear();//清空容器元素
  • erase(pos);//删除pos迭代器所指的元素,返回下一个元素的迭代器
  • erase(beg, end);//删除区间的所有元素,返回下一个元素的迭代器
  • erase(key);//删除容器中键值为key的键值对

可以用key[xx]的方式获取键值对。

5.map查找和统计

函数原型:

  • find(key);//查找key是否存在,存在返回该键的元素的迭代器,不存在返回map.end()
  • count(key);//统计key的元素个数:对map只能是0或1

6.map容器排序

​ map容器默认排序规则是按key值升序排列,利用仿函数可以改变排序规则,对于自定义数据类型,map必须制定排序规则:

class compare{
public:
  	bool operator()(int v1, int v2){
      	return v1 > v2;//降序
    }
}

7.员工分组信息显示案例:

class Worker{
public:
    string mName;
    int mSalary;
};
//创建员工
void create(vector<Worker>&v){
    string nameSeed = "ABCDEFGHIJ";
    for (int i = 0; i < 10; ++i) {
        Worker worker;
        worker.mName = "员工";
        worker.mName += nameSeed[i];
        worker.mSalary = rand()%10000 + 10000;//10000~19999
        v.push_back(worker);
    }
}
//员工分组
void group(vector<Worker>&v, multimap<int, Worker>&mp){
    for(vector<Worker>::iterator it = v.begin();it != v.end();it ++){
        //产生随机部门编号
        int depId = rand()%3;//0 1 2
        //将员工插入到分组中
        mp.insert(make_pair(depId, *it));
    }
}
//打印员工姓名和工资
void printVector(vector<Worker>&v){
    for(vector<Worker>::iterator it = v.begin();it != v.end();it ++){
        cout << "姓名:" << it->mName << " 工资:" << it->mSalary << endl;
    }
}
//分组打印员工信息
void printWorkerOfGroup(multimap<int, Worker>&mp){
    multimap<int, Worker>::iterator it;
    for (int i = 0; i < 3; ++i) {//循环打印三个部门的信息
        cout << "部门" << i << ":" << endl;
        it  = mp.find(i);
        int count = mp.count(i);
        int index = 0;
        for(;it != mp.end() && index < count;it ++, index ++){
            cout << "部门:" << it->first << " 姓名:" << it->second.mName
                 << " 工资:" << it->second.mSalary <<endl;
        }
    }
}
int main(){
    vector<Worker>v;
    create(v);//创建员工
    printVector(v);//输出员工信息
    cout << "-----------------------" << endl;
    multimap<int, Worker>mp;
    group(v, mp);//员工分组
    printWorkerOfGroup(mp);//分组显示员工的部门、姓名、工资
}

四、函数对象

(一)函数对象(仿函数)

1.函数对象概念

  • 重载函数调用操作符的类,其对象称为函数对象

  • 函数对象使用重载的()时,行为类似函数调用,也叫仿函数

  • 函数对象(仿函数)是一个类,不是函数

2.函数对象使用

特点:

  • 函数对象在使用时,可以像普通函数那样调用,可以有参、有返回值
  • 函数对象超出普通函数的概念,函数对象内部可以有自己的状态(成员属性等)
  • 函数对象可以作为参数传递
class MyAdd{
public:
    //1.使用时像普通函数,可有参 有返回
    int operator()(int v1, int v2){
        return v1 + v2;
    }
};
//2.函数对象内部可以有自己的状态(成员属性)
class MyPrint{
public:
    MyPrint(){
        this->count = 0;
    }
    void operator()(string test){
        cout << test << endl;
        this->count ++;
    }
    int count;//内部自己的状态,记录函数被调用次数
};
//3.函数对象可以作为参数传递
void doPrint(MyPrint &mp, string test){
    mp(test);
}

int main(){
    MyAdd myAdd;//1.这是函数对象
    cout << myAdd(1, 5);
    //2.打印调用次数
    MyPrint myPrint;
    myPrint("yeah!");
    myPrint("yeah!");
    myPrint("yeah!");
    myPrint("yeah!");
    cout << myPrint.count << endl;
    //3.作为对象传递
    MyPrint myPrint1;
    doPrint(myPrint1,"hello~~");

}

(二)谓词

1.谓词概念

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

2.谓词示例

class FiveBig{//仿函数,查找大于5的数
public:
    bool operator()(int val){//bool类型,一个参数
        return val > 5;
    }
};

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

int main(){
    vector<int>v;
    for (int i = 0; i < 10; ++i) {
        v.push_back(i);
    }
    //创建匿名对象
    vector<int>::iterator it = find_if(v.begin(), v.end(),FiveBig());
    if (it == v.end()){
        cout << "未找到" << endl;
    }else{
        cout << "找到:" << *it << endl;
    }

    vector<int>v2;
    v2.push_back(4);
    v2.push_back(1);
    v2.push_back(3);
    sort(v2.begin(), v2.end(), Compare());
    for(vector<int>::iterator it = v2.begin();it != v2.end();it++){
        cout << *it << " ";
    }
}

(三)内建函数对象

1.意义

STL内置了一些函数对象,包括算术仿函数、关系仿函数、逻辑仿函数,这些仿函数所产生的对象,用法和一般函数完全相同,使用时引入头文件functional

2.算数仿函数

实现size运算,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一元仿函数
negate<int>n;
int ret = n(3);
cout << ret << endl;//输出-3

//加法 二元仿函数
plus<int>p;//只允许两个相同类型
cout << p(4,6) << endl;

3.关系仿函数

仿函数原型:

  • 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>//小于等于
int main(){
    vector<int>v;
    v.push_back(5);
    v.push_back(2);
    v.push_back(1);
    //使用关系仿函数greater 降序排列
    sort(v.begin(), v.end(), greater<int>());
}

4.逻辑仿函数(用途少)

  • template<class T> bool logica_and<T>//逻辑与
  • template<class T> bool logical_or<T>//逻辑或
  • template<class T> bool logical_not<T>//逻辑非
vector<bool>v1;
v1.push_back(true);
v1.push_back(false);
//将v1中元素取反后到v2中
vector<bool>v2;
v2.resize(v1.size());//先开辟空间,再搬运
transform(v1.begin(), v1.end(), v2.begin(),logical_not<bool>());

五、STL常用算法

概述:

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

(一)常用遍历算法

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

1.for_each算法(常用)

函数原型:

for_each(iterator beg, iterator end, _func);//开始/结束迭代器beg/end,_func函数或函数对象

//普通函数,打印输出
void printVec(int val){
    cout << val << " ";
}
//仿函数,打印输出
class PrintFunc{
public:
    void operator()(int val){
        cout << val << " ";
    }
};
int main(){
    vector<int>v;
    v.push_back(2);
    v.push_back(1);
    v.push_back(3);
    for_each(v.begin(), v.end(), printVec);//普通函数
    for_each(v.begin(), v.end(), PrintFunc());//仿函数
}

2.transform算法

​ 搬运容器到另一个容器中。

函数原型:

transform(iterator beg1, iterator end1,iterator beg2, _func);

//原容器开始和结束迭代器;目标容器开始迭代器;_func函数或函数对象

class Transform{
public:
    int operator()(int val){
        return val + 10;//搬运过程中可以进行运算
    }
};
class PrintFun{
public:
    void operator()(int val){
        cout << val << " ";
    }
};
int main(){
    vector<int>v;
    v.push_back(3);
    v.push_back(1);
    v.push_back(2);
    vector<int>vTarget;
    vTarget.resize(v.size());//搬运前提前开辟空间
    transform(v.begin(), v.end(), vTarget.begin(),Transform());
    for_each(vTarget.begin(), vTarget.end(), PrintFun());
}

(二)常用查找算法

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

1.find算法

​ 查找指定元素,找到返回指定元素的迭代器,找不到返结束迭代器。

函数原型:

find(iterator beg, iterator end, value);

//beg/end:开始/结束迭代器,value查找的元素

示例:查找内置数据类型和自定义数据类型

class Person{
public:
    Person(string name, int age){
        this->mName = name;
        this->mAge = age;
    }
    //重载==,底层find函数才知道如何对比Person类型
    bool operator==(const Person &p){
        if(this->mName == p.mName && this->mAge == p.mAge){
            return true;
        } else{
            return false;
        }
    }
    string mName;
    int mAge;
};

int main(){
    //——————————查找内置数据类型————————————
    vector<int>v;
    v.push_back(5);
    v.push_back(1);
    v.push_back(3);

    vector<int>::iterator it = find(v.begin(), v.end(), 5);
    if(it == v.end()){
        cout << "没找到"<< endl;
    } else{
        cout << "找到:" << *it << endl;
    }

    //——————————查找自定义数据类型————————————
    vector<Person>vPerson;
    Person p1("小李", 20);
    Person p2("小刘", 14);
    Person p3("小孙", 34);
    vPerson.push_back(p1);
    vPerson.push_back(p2);
    vPerson.push_back(p3);
    //下面的find需要重载==运算符
    vector<Person>::iterator its = find(vPerson.begin(), vPerson.end(), p2);
    if(it == v.end()){
        cout << "没找到!" << endl;
    } else{
        cout << "找到:" << its->mName << its->mAge << endl;
    }
}

2.find_if算法

​ 按条件查找元素,找到返回指定元素的迭代器,找不到返结束迭代器。

函数原型:

find_if(iterator beg, iterator end, _Pred);

//beg/end:开始/结束迭代器,_Pred:函数或者谓词(返回bool类型的仿函数)

示例:条件查找内置数据类型和自定义数据类型

class Person{
public:
    Person(string name, int age){
        this->mName = name;
        this->mAge = age;
    }
    string mName;
    int mAge;
};
class GreaterFive{//谓词,大于5
public:
    bool operator()(int val){
        return val > 5;
    }
};
class AgeGreater20{//谓词,年龄大于20
public:
    bool operator()(const Person &p){
        return p.mAge >20;
    }
};

int main(){
    //——————————条件查找 内置数据类型————————————
    vector<int>v1;
    v1.push_back(5);
    v1.push_back(6);
    v1.push_back(3);

    vector<int>::iterator it1 = find_if(v1.begin(), v1.end(),GreaterFive());
    if(it1 == v1.end()){
        cout << "没找到"<< endl;
    } else{
        cout << "找到大于5的数:" << *it1 << endl;
    }

    //——————————条件查找 自定义数据类型————————————
    vector<Person>v2;
    Person p1("小李", 20);
    Person p2("小刘", 14);
    Person p3("小孙", 34);
    v2.push_back(p1);
    v2.push_back(p2);
    v2.push_back(p3);

    vector<Person>::iterator it2 = find_if(v2.begin(), v2.end(), AgeGreater20());
    if(it2 == v2.end()){
        cout << "没找到!" << endl;
    } else{
        cout << "找到:" << it2->mName << it2->mAge << endl;
    }
}

3.adjacent_find算法

​ 查找相邻重复元素,返回相邻元素的第一个位置的迭代器。

函数原型:

adjacent_find(iterator beg, iterator end);

//beg/end:开始/结束迭代器

int main(){
    //——————————条件查找 内置数据类型————————————
    vector<int>v;
    v.push_back(0);
    v.push_back(3);
		v.push_back(3);
    v.push_back(1);
    v.push_back(3);
    vector<int>::iterator pos = adjacent_find(v.begin(),v.end());
    if (pos == v.end()){
        cout << "未找到相邻重复元素!" << endl;
    } else{
        cout << "找到相邻重复元素:" << *pos << endl;
    }
}

4.binary_search算法

​ 二分查找法,查找指定元素是否存在,存在返回true,不存在返回false。在无序序列中不可用,无序序列中查找结果未知。

函数原型:

bool binary_search(iterator beg, iterator end,value);

//beg/end:开始/结束迭代器,value:查找的元素

int main() {
    //——————————二分查找,容器为有序序列————————————
    vector<int> v;
    for (int i = 0; i < 10; ++i) {
        v.push_back(i);
    }
    bool ret = binary_search(v.begin(), v.end(),9);
    if (ret) {
        cout << "找到元素!" << endl;
    } else {
        cout << "未找到相邻重复元素!" << endl;
    }
}

5.count算法

​ 统计元素个数。

函数原型:

count(iterator beg, iterator end, value);

示例:统计内置数据类型和自定义数据类型

class Person{
public:
    Person(string name, int age){
        this->mName = name;
        this->mAge = age;
    }
    //重载==,底层count函数才知道如何对比Person类型
    bool operator==(const Person &p){
        if(this->mAge == p.mAge){
            return true;
        } else{
            return false;
        }
    }
    string mName;
    int mAge;
};
int main() {
    //——————————1.查找内置数据类型————————————
    vector<int> v;
    v.push_back(2);
    v.push_back(3);
    v.push_back(1);
    v.push_back(3);
    int num1 = count(v.begin(), v.end(),3);
    cout << num1 << endl;

    //——————————2.查找自定义数据类型————————————
    vector<Person>v2;
    Person p1("二哥", 20);
    Person p2("三哥", 14);
    Person p3("大哥", 34);
    Person p("大A",14);//用于检测
    v2.push_back(p1);
    v2.push_back(p2);
    v2.push_back(p3);
    int num2 = count(v2.begin(), v2.end(), p);
    cout << "和大A同岁的人个数" << num2 << endl;
}

6.count_if算法

​ 按条件统计元素个数。

函数原型:

count_if(iterator beg, iterator end, _Pred);

//_Pred:谓词

示例:条件统计内置数据类型和自定义数据类型的个数

class Person{
public:
    Person(string name, int age){
        this->mName = name;
        this->mAge = age;
    }
    string mName;
    int mAge;
};
class GreaterTen{//谓词,大于10
public:
    bool operator()(int val){
        return val > 10;
    }
};
class AgeGreater20{//谓词,年龄大于20
public:
    bool operator()(const Person &p){
        return p.mAge >20;
    }
};

int main(){
    //——————————条件统计 内置数据类型————————————
    vector<int>v1;
    v1.push_back(9);
    v1.push_back(20);
    v1.push_back(22);
    v1.push_back(11);
    int num1 = count_if(v1.begin(), v1.end(),GreaterTen());
        cout << "大于10的个数:"<< num1 << endl;

    //——————————条件统计 自定义数据类型————————————
    vector<Person>v2;
    Person p1("小马", 12);
    Person p2("小猪", 43);
    Person p3("小牛", 34);
    v2.push_back(p1);
    v2.push_back(p2);
    v2.push_back(p3);

    int num2 = count_if(v2.begin(), v2.end(), AgeGreater20());
    cout << "大于20岁的人员个数:"<< num2 << endl;

}

(三)常用排序算法

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

1.sort算法

函数原型:

sort(iterator beg, iterator end, _Pred);

//_Pred谓词,可填可不填

2.random_shuffle算法

函数原型:

random_shuffle(iterator beg, iterator end);

3.merge算法

函数原型:

merge(iterator beg1, iterator end1, iterator beg2, iterator end2,iterator dest);

//dest:目标容器开始迭代器

void printVector(int val){
    cout << val << " ";
}

int main() {
    //——————————merge合并两个容器到另一个容器,必须为有序序列————————————
    vector<int> v1;
    vector<int> v2;
    vector<int> vTarget;//目标容器
    for (int i = 0; i < 10; ++i) {
        v1.push_back(i);
        v2.push_back(i+2);
    }
    //先给目标容器开辟空间
    vTarget.resize(v1.size() + v2.size());

    merge(v1.begin(), v1.end(), v2.begin(), v2.end(),vTarget.begin());
    for_each(vTarget.begin(), vTarget.end(), printVector);

}

4.reverse算法

函数原型:

reverse(iterator beg, iterator end);```

(四)常用拷贝替换算法

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

1.copy算法

函数原型:

copy(iterator beg, iterator end, iterator dest);//dest:目标起始迭代器,目标容器提前开辟空间

2.replace算法

函数原型:

replace(iterator beg, iterator end, oldvalue, newvalue);

3.replace_if算法

函数原型:

replace_if(iterator beg, iterator end, _Pred, newvalue);

void printVect(int val){
    cout << val << " ";
}
class GreaterFive{//谓词,大于5
public:
    bool operator()(int val){
        return val > 5;
    }
};

int main(){
    vector<int>v;
    for (int i = 0; i < 10; ++i) {
        v.push_back(i);
    }
    replace_if(v.begin(), v.end(),GreaterFive(),100);
    for_each(v.begin(), v.end(), printVect);
}

4.swap算法

swap(container c1, container c2);//交换同类型的容器

(五)常用算数生成算法

算数生成算法是小型算法,在头文件<numeric>中

  • accumulate();//计算容器元素累计总和,返回int
  • fill();//向容器中添加元素

1.accumulate算法

accumulate(iterator beg, iterator end, value);//value:起始累加值

2.fill算法

fill(iterator beg, iterator end, value);//value:填充值

(六)常用集合算法

  • set_intersection();//求两个容器的交集,放到另一个容器中
  • set_union();//求两个容器的并集
  • set_difference();//求两个容器的差集

三个函数的返回值是:新容器的最后位置的迭代器。2个原容器必须是有序序列。

1.set_intersection算法

set_intersection(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);

目标容器开辟空间为小容器size()。

2.set_union算法

set_union(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);

目标容器开辟空间为两容器和。

3.set_difference算法

set_difference(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);

目标容器开辟空间为大容器size()。

C++学习笔记(基础篇) C++在C语言的基础上,弥补了C语言存在的一些缺陷,增加了面向对象的特征,是一种面向过程性与面向对象性相结合的程序设计语言。 阅读详情

相关推荐

C++编程基础完整学习笔记资料包.zip

C++是一种通用的编程语言,是C语言的超集,增加了面向对象编程(OOP)的特性。它由Bjarne Stroustrup于1980年代初期开发,在1990年代成为广泛使用的商业标准。本章将为你提供一个关于C++编程语言的概览,包括它的历史、特点、以及如何适用于现代软件开发。C++作为一种静态类型、编译式、通用的编程语言,在过去几十年里一直是软件开发的重要工具。其丰富的库、面向对象的特性以及性能上的优势使其在系统软件、游戏开发、实时物理模拟等领域得到了广泛应用。

weixin_42466723的博客 726

C++笔记--输入输出流

C++本身不提供输入输出操作,由标准库提供;这些功能支持程序中独立于设备的输入输出操作。流是程序中输入或输出设备的抽象表示,输入或输出设备是数据的来源或目的地。可以把流看作在外部设备和计算机内存之间流动的一系列字节。基本上,所有的输入和输出都是在某个外部设备上读取或写入的一系列字节。 数据传输模式 在流中传入和传出数据有两种模式:文本模式和二进制模式。在文本模式中,数据解释为一系列字符,这些字符组织为一行或多行文本,并用换行符‘\n’断开。在文本模式中,当字符在物理设备上读写时,流可以传送...

ljt2724960661的博客 305

显示IE插件 BHO ShowBHOInfo

源码。 显示系统中存在的BHO(即IE插件)。 用VC++6.0编写。

Python 函数-第13篇(9.19)

函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。 函数能提高应用的模块性,和代码的重复利用率。你已经知道Python提供了许多内建函数,比如print()。但你也可以自己创建函数,这被叫做用户自定义函数。 定义一个函数 你可以定义一个由自己想要功能的函数,以下是简单的规则: 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号()。 任何传入参数和自变量必须放在圆括号中间。圆括号之间可以用于定义参数。 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。 函数内容以冒号

qq_41585840的博客 312

Fluent DPM模型保姆级教程:三通管颗粒流动模拟,从网格导入到轨迹图生成

本文提供Fluent DPM模型在三通管颗粒流动模拟中的保姆级教程,涵盖从网格导入到轨迹图生成的全流程。通过详细步骤和参数设置,帮助用户掌握DPM模型在颗粒流动模拟中的关键技巧,适用于化工、能源等领域的工程应用。

weixin_30615767的博客 579

别再手动改颜色了!用这个Word宏,一键搞定Zotero参考文献超链接的蓝色样式

本文介绍如何利用Word宏一键修改Zotero参考文献的超链接颜色,解决手动调整的繁琐问题。通过详细的VBA代码解析和高级定制方案,帮助科研人员快速实现文献引用的自动化格式优化,提升论文排版效率。特别适合需要批量处理APA等标准格式蓝色超链接的用户。

weixin_30875157的博客 515

C++文件读写详解

1.打开文件(ofstream || ifstream)。如果想读文件则需要建立 ifstream类的对象。如果想写文件则需要建立ofstream类的对象。打开文件的方式有多种具体如下:读文件一般以 ios::in 方式打开写文件一般以 ios::out方式打开,这种打开方式会清空原数据,如果不想清空则需要以ios::app方式打开eg:题目:打开当前目录下的data.txt文件(以读的方式打开)...

愿你饱经冷暖,仍保纯真 1623

CentOS校园网Web Portal认证自动化实战

Web Portal认证是一种基于HTTP重定向与表单提交的强制网络准入机制,其原理是网关劫持未认证流量并跳转至登录页,验证通过后通过Cookie维持会话授权。该技术广泛应用于高校、机场、酒店等场景,核心价值在于细粒度访问控制与用户身份绑定。典型应用包括Linux命令行环境下的校园网接入、无GUI设备的自动联网、以及实验室集群的集中认证管理。本文聚焦CentOS系统,深入解析curl、cookie持久化、动态表单参数提取等关键技术环节,提供可调试、可复现、可工程化的完整认证流水线。

weixin_30522983的博客 265

C++纯虚函数和抽象类

抽象类:纯虚函数存在于最远的的父类,这种类称为抽象类。纯虚函数:只定义其功能不实现功能的函数称为纯虚函数。

weixin_47340034的博客 148

CST毫米波雷达ADAS仿真:从直角反射器到距离多普勒(RD图)的实战解析

本文通过CST Studio Suite实战解析毫米波汽车雷达ADAS仿真,详细演示了从构建直角反射器目标、参数化定义速度、进行参数扫描到生成距离多普勒图(RD图)的全过程。文章深入探讨了雷达关键参数(如距离分辨率、速度分辨率)的设计权衡与工程逻辑,并提供了单位统一、速度范围设定等实用避坑指南,帮助工程师高效完成雷达系统性能的前期验证与优化。

weixin_29292535的博客 502

【PyTorch版本兼容性指南】如何在不同版本中正确使用torch.nn.attention

本文详细解析了PyTorch版本兼容性问题,特别是torch.nn.attention模块在不同版本中的使用差异。针对常见的ModuleNotFoundError错误,提供了三种实用解决方案:升级PyTorch版本、使用自定义实现和源码移植方案,并分享了最佳实践建议,帮助开发者有效避免兼容性问题。

weixin_29084231的博客 472

[学习笔记] 1. C++ / CPP基础入门

C++入门知识

Le0v1n 的博客 3585

LabVIEW电压信号采集系统:从零搭建到科研实战

本文详细介绍了如何从零开始搭建一个基于LabVIEW的电压信号采集系统。内容涵盖硬件选型(如NI USB-6002数据采集卡)、软件环境配置、核心程序开发(包括多通道同步采集、数据自动保存)以及高级功能优化。系统支持自定义采样率与时长,并提供了完整的LabVIEW程序框图和配套的Python数据分析脚本,旨在帮助科研人员和工程师快速构建稳定、高效的专属数据采集方案,提升实验自动化水平。

gold8的专栏 461

学习笔记—C++—入门基础()

C++”是一种多功能的编程语言,它起源于1980年代初,由Bjarne Stroustrup在C语言的基础上发展而来。C++最初被设计为支持面向对象编程的C语言变体,后来经过多次迭代和标准化,发展成为一种广泛应用于系统软件、游戏开发、嵌入式系统等领域的语言。C++的发展经历了多个重要阶段,包括C++98、C++03、C++11、C++14、C++17、C++20和即将到来的C++23标准.定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接⼀对{}即可,{}中 即为命名空间的成员。

2401_83231468的博客 2628

Xilinx平台SRIO介绍(五)示例工程Example Design介绍

理解IP的使用,少不了Example Design的学习。

数字IC/FPGA杂货铺,找一找,或许有你想要的~ 1万+

NSIS教程(6): 使用第三方插件

NSIS内置了很多指令,这些指令可以满足我们基本需求,但难免会遇到NSIS没有内置的功能,如进程操作,后台运行(即多线程),7z压缩等等,这个时候我们就需要借助第三方插件来满足我们所需要的功能了。 一、插件获取 NSIS官方收录插件的网址是:http://nsis.sourceforge.net/Category:Plugins,我们可以在这个网站上查找和下载我们需要的插件,该网站上针对每

while(1) { smile(); } 9万+

c++学习笔记——c++基础

定义命名空间,需要使⽤到namespace关键字,后⾯跟命名空间的名字,然后接⼀对{}即可,{}中即为命名空间的成员。命名空间中可以定义变量/函数/类型等。namespace本质是定义出⼀个域,这个域跟全局域各⾃独⽴,不同的域可以定义同名变量,所以下⾯的rand不在冲突了。// 变量// 函数// 类型int val;// 指定访问abc中的rand// ::称为域作用限定符return 0;C++中的域有函数局部域全局域命名空间域类域;域影响的是编译时。

ajole的博客 1208

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

C++学习笔记:从基础到进阶

《C++核心语法与特性摘要》 本文系统梳理C++基础与进阶知识:1️⃣基础语法涵盖变量类型、运算符、控制流及函数;2️⃣面向对象重点解析类与对象、继承多态;3️⃣指针与引用对比其内存操作差异;4️⃣介绍数组、字符串及STL容器算法。进阶部分涉及模板编程、异常处理、智能指针等现代特性。学习建议强调指针理解、STL实践和合理运用面向对象设计原则(组合优于继承)。全文以代码示例辅助说明,适合快速回顾核心概念。

qq_63028796的博客 1329

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

【C++】黑马程序员-C++基础语法学习笔记

根据黑马程序员C++课程内容,结合讲义,将自己学习C++的过程中将自己觉得有必要记下的笔记进行整理,方便复习回顾,编程环境为VSCode。编写代码: 运行程序:作用: 在代码中加一些说明和解释,方便自己或其他程序员程序员阅读代码两种格式通常放在一行代码的上方,或者一条语句的末尾,对该行代码说明 多行注释: /* 描述信息 */ 通常放在一段代码的上方,对该段代码做整体说明 多行注释快捷键: ①注释/取消:Ctrl+Shift+/ ②注释:Ctrl+K+C ③取消注释:Ctrl+K+U作用:给一段指定的内

qq_62361050的博客 1792

c++基础学习笔记01

这段内容是作者本人在学习c++的过程中,所记录的一些笔记,接下来,我会逐渐整理上传笔记,希望我的笔记可以帮助到更多想要学习的人,转载请告诉本人

weixin_60181545的博客 2186

C++学习笔记之指针(基础)

并且,在进行关系比较前,需要。

韵律咖啡馆 1209

C++学习笔记_1 C++基础

持续发布的C++学习笔记

Disthinker的博客 495

C++基础入门学习笔记

C++基础入门 1.1 hello world #include<iostream> using namespace std; int main() { cout << "Hello world" << endl; system("pause"); return 0; } 1.2 注释 作用:在代码中加一些说明和解释,方便自己或其他程序员程序员阅读代码 两种格式 单行注释:// 描述信息 通常放在一行代码的上方,或者一条语句的末尾,对该行代码说明

weixin_43544679的博客 8478

C++基础学习笔记

创建 .h 的后缀名的头文件创建 .cpp 的后缀名的源文件在头文件中写函数的声明在源文件中写函数的定义如下:abc.habc.cpp。

坚持不懈的大白的博客 1001

C++ 基础 学习笔记

所谓数组,就是一个集合,里面存放了相同类型的数据元素特点1:数组中的每个==数据元素都是相同的数据类型==特点2:数组是由==连续的内存==位置组成的数据类型 数组名[ 数组长度 ];数据类型 数组名[ 数组长度 ] = { 值1,值2 ...};数据类型 数组名[ ] = { 值1,值2 ...};//定义方式1//数据类型 数组名[元素个数];//利用下标赋值//利用下标输出//第二种定义方式。

m0_56167380的博客 145
上一篇: C++基础学习笔记(上)
下一篇: (三)MySQL学习笔记——约束
MegaBytes
博客等级 码龄8年 45粉丝 115原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值