C++ 超详细知识点梳理+思维导图

时间:2021-04-01 01:17:12

一篇文章带你打开C++的大门

本文为博主复习完C++基础知识后总结的知识点,重要知识点都有简单demo示例。欢迎点赞收藏,思维导图层级太多,页面上放不下,若有需要直接私信哦。

1. 整体知识网络

C++是建立在C语言基础上的开发语言,在C语言中,对于基本的数据类型、常用的程序结构(顺序、分支、循环)、数组、函数、结构体、指针这些基础内容进行了解之后,就可以开始C++的学习。

对于C++的学习,首先是面向对象的基础学习,建立单个的类,然后探讨如何建立类与类之间的关系,利用C++的语言特性,封装、继承和多态。对基础的语言知识进行了了解之后,需要考虑的是软件的核心价值在于软件的重用性,而泛型编程是提升软件重用性的一大利器。

针对面向对象和泛型编程两条技术类型,本文梳理了C++的一些核心知识。

C++ 超详细知识点梳理+思维导图

2. 基础知识点

2.1 关键字new和delete

new和delete用于堆上开辟内存空间,成对使用。尤其注意数组类型的数据,且内存不可重复释放。发生继承关系时父类析构函数处理为虚函数就是为了子类堆上内存的释放。

//变量使用
int* p = new int;
*p = 2;
// int* p = new int(2);
delete p;

//数组使用
int* arr = new int[10];
delete[] arr;

2.2 引用

引用相当于给变量起一个别名,本质上是指针常量的使用。指针常量可以改变指向的内容,但是不可以改变指针(地址)的值,因此一旦建立起引用关系,不能将一个引用再绑定到其他变量。

  • 创建引用必须要初始化
  • 引用关系建立之后不可以更改
  • 引用作为函数参数,形参可修饰实参
  • 函数返回引用,可作为左值,实现链式操作
    int a = 10;
    int c = 20;
    int& b = a;
    //b = c; 是赋值操作,无法建立引用关系
int& INC_Val(int& val)
{
    val++;
}

int main() {
	
    int a = 10;
    INC_Val(a);

    //返回引用可以作为左值,相当于a = 20
    //INC_Val(a) = 20;

    //链式操作
    //INC_Val(INC_Val(a));

    cout << a << endl;
	system("pause");

	return 0;
}

2.3 函数重载

同一个作用域下,函数名称相同,函数参数类型、参数个数、及参数顺序可以构成重载。

int Add(int a, int b)
{
    return a + b;
}

int Add(int a, int b, int c)
{
    return a + b + c;
}

/*
重载遇到默认参数,有歧义无法重载,Add(10, 20)不知道是调用哪一个
int Add(int a, int b, int c = 10)
{
    return a + b + c;
}
*/

int main()
{

    int a = 10;
    int b = 20;
    int c = 30;
    cout << Add(10, 20) << endl;
    system("pause");

    return 0;
}

函数参数的const可以构成重载

int fun(int &a)
{
    return (++a);
}

int fun(const int &a)
{
    cout << "调用const重载" << endl;
    return 0;
}

int main()
{

    int a = 10;
    const int b = 20;
    //调用fun(int &a)
    cout << fun(a) << endl;
    //调用fun(const int &a)
    cout << fun(b) << endl;
    system("pause");

    return 0;
}

2.4 运算符重载

运算符重载是最常用的操作,为一些自定义类型的数据提供标准的运算符操作方法。

全局函数运算符重载

class Person
{
public:
    string m_name;
    int m_age;

    Person(string name = "jack", int age = 15) : m_name(name), m_age(age){};
    ~Person(){};
};

inline ostream &operator<<(ostream &os, const Person &p)
{
    os << "m_name is " << p.m_name << endl;
    os << "m_age is " << p.m_age << endl;
    return os;
}

int main()
{
    Person ps1("zhangsan");
    Person ps2("lisi", 20);
    cout << ps1 << ps2 << endl;
    system("pause");

    return 0;
}

成员函数运算符重载

class Person
{
public:
    string m_name;
    int m_age;

    Person(string name = "jack", int age = 15) : m_name(name), m_age(age){};
    ~Person(){};

    bool operator==(const Person &pother);
};
inline bool Person::operator==(const Person &pother)
{
    if (this->m_age == pother.m_age)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

inline ostream &operator<<(ostream &os, const Person &p)
{
    os << "m_name is " << p.m_name << endl;
    os << "m_age is " << p.m_age << endl;
    return os;
}

int main()
{
    Person ps1("zhangsan");
    Person ps2("lisi", 22);
    cout << ps1 << ps2 << endl;
    if (ps1 == ps2)
    {
        cout << "年龄相同" << endl;
    }
    else
    {
        cout << "年龄不相同" << endl;
    }

    system("pause");

    return 0;
}

3. 面向对象

3.1 构造函数

创建一个类,至少需要提供三个函数:构造函数、析构函数、拷贝构造函数。如果没有自行定义这些函数,C++会提供默认的函数。默认的构造函数和析构函数都是空实现,默认的拷贝构造函数是逐个字节的浅拷贝。
类的成员中有指针,一定要有拷贝构造和析构函数。

class Person
{
public:
    string m_name;
    int m_age;
    char *m_ID;

    Person(string name = "jack", int age = 15, char *id = "A001");
    Person(const Person &pOther);
    Person &operator=(const Person &pOther);
    ~Person();
};

Person::Person(string name, int age, char *id)
{
    m_ID = new char[strlen(id) + 1];
    strcpy(m_ID, id);
    cout << "构造函数调用" << endl;
}

//拷贝构造
Person::Person(const Person &pOther)
{
    this->m_age = pOther.m_age;
    this->m_name = pOther.m_name;
    this->m_ID = new char[strlen(pOther.m_ID) + 1];
    strcpy(this->m_ID, pOther.m_ID);
}

//拷贝赋值
inline Person &
Person::operator=(const Person &pOther)
{
    if (this == &pOther)
    {
        return *this;
    }
    else
    {
        delete[] m_ID;
        this->m_age = pOther.m_age;
        this->m_name = pOther.m_name;
        this->m_ID = new char[strlen(pOther.m_ID) + 1];
        strcpy(this->m_ID, pOther.m_ID);
    }
}

Person::~Person()
{
    delete[] m_ID;
    cout << "析构函数调用" << endl;
}

inline ostream &operator<<(ostream &os, const Person &p)
{
    os << "m_name is " << p.m_name << endl;
    os << "m_age is " << p.m_age << endl;
    os << "m_ID id " << p.m_ID << endl;
    return os;
}

int main()
{
    Person p1("zhangsan", 28, "A005");
    Person p2(p1);
    Person p3 = p2;
    cout << p1 << endl;
    cout << p2 << endl;
    system("pause");

    return 0;
}

3.2 static

成员变量和成员函数加了static,表示属于类。static成员变量只有一份,在类中申明,需要在类外进行初始化。

class Person
{
public:
    string m_name;
    int m_age;
    static int m_groupNum;

    Person(){};
    ~Person(){};
};

//类中申明,类外main初始化
int Person::m_groupNum = 10;

int main()
{
    cout << Person::m_groupNum << endl;

    //可以通过类调用
    Person::m_groupNum = 20;
    cout << Person::m_groupNum << endl;

    //也可以通过对象调用
    Person p1;
    p1.m_groupNum = 30;
    cout << Person::m_groupNum << endl;
    system("pause");

    return 0;
}

静态成员函数中只能使用类中的静态成员变量

class Person
{
private:
    static int m_groupNum;

public:
    string m_name;
    int m_age;

    Person(){};
    ~Person(){};

    static int GetGropuNum(void) { return m_groupNum; };
};

int Person::m_groupNum = 10;

int main()
{
    cout << Person::GetGropuNum() << endl;
    system("pause");

    return 0;
}

在类中,只有非静态成员变量才占用对象内存空间,静态成员变量、静态成员函数和非静态成员函数都不占用对象内存空间。

class Person
{
private:
    static int m_groupNum;

public:
    int m_age;

    Person(){};
    ~Person(){};

    static int GetGropuNum(void) { return m_groupNum; };
    int GetAge(void) { return m_age; };
};

int Person::m_groupNum = 10;

int main()
{
    Person p1;
    cout << sizeof(p1) << endl;

    system("pause");
    return 0;
}

3.3 this指针

this指针是隐藏在非静态成员函数中的一个指针
在成员函数中返回对象的引用,可以直接使用*this

3.4 const

const 常函数 常对象

class Person
{
public:
    int m_age;

    Person(){};
    ~Person(){};
    //常函数,函数内不可修改成员变量
    int GetAge(void) const { return m_age; };
    int SetAge(int age) { m_age = age; };
};

int main()
{
    const Person p1;
    // p1.m_age = 10; 不可实现
    cout << p1.m_age << endl; // 常对象只能读取
    //p1.SetAge(10); 不可调用
    cout << p1.GetAge() << endl; //常对象只能访问常函数

    system("pause");
    return 0;
}

3.5 友元

友元类

class Person;
class GirlFriend
{
    //Person是GirlFriend类的朋友,Person可以访问GirlFriend的私有成员
    friend class Person;

private:
    int m_age;
    string m_name;

public:
    GirlFriend(int age = 18, string name = "XiaoXiao") : m_age(age), m_name(name){};
    int GetAge() { return m_age; };
    void SetAge(int age) { m_age = age; };
};

class Person
{
public:
    GirlFriend *m_girl;

    Person()
    {
        m_girl = new GirlFriend();
    };
    ~Person()
    {
        delete m_girl;
    };
    int GetGirlFriendAge()
    {
        return m_girl->m_age;//友元关系,可以访问私有成员
        // return m_girl->GetAge();//如果不是友元,只能通过public接口函数访问
    }
};

int main()
{

    Person p1;
    p1.m_girl->SetAge(18);
    cout << p1.GetGirlFriendAge() << endl;

    system("pause");
    return 0;
}

3.6 继承关系–name hiding

发生继承关系时,子类和父类中如果有同名函数,会发生name hiding,即“隐藏”。

class Person
{
private:
    int m_age;

public:
    int m_sex;
    int GetAge() { return m_age; };
    Person(int age = 18, int sex = 1) : m_age(age), m_sex(sex){};
    ~Person(){};
};

class Employee : public Person
{
public:
    int m_age;
    int m_groupNum;
    Employee(int age = 20, int num = 4) : m_age(age), m_groupNum(num), Person(){};
    ~Employee(){};
    int GetAge() { return m_age; };
};

int main()
{
    Employee p1;
    //发生隐藏,访问的是子类中的函数
    cout << p1.GetAge() << endl;
    //如果想要访问父类中的函数,要加上作用域
    cout << p1.Person::GetAge() << endl;
    system("pause");
    return 0;
}

3.7 继承关系–菱形继承

Data是多继承,继承了BaseABaseB,而BaseABaseB都是基类Base派生而来。当这样的继承关系发生时,基类中的数据就m_dataData中就是两份,浪费内存,也不利于维护,这样的继承不是我们想要的。

class Base
{
public:
    int m_data;
    Base(){};
    ~Base(){};
};

class BaseA : public Base
{
public:
    int m_dataA;
    BaseA(){};
    ~BaseA(){};
};

class BaseB : public Base
{
public:
    int m_dataB;
    BaseB(){};
    ~BaseB(){};
};

class Data : public BaseA, public BaseB
{
public:
    Data(){};
    ~Data(){};
};

int main()
{
    Data data;
    data.BaseA::m_data = 10;
    data.BaseB::m_data = 20;
    //需要添加作用域才能访问,两份数据不同,A中输出10
    cout << data.BaseA::m_data << endl;
    //B中输出20
    cout << data.BaseB::m_data << endl;
    system("pause");
    return 0;
}

对于这种情况,通过虚继承可以达到我们理想的效果

class Base
{
public:
    int m_data;
    Base(){};
    ~Base(){};
};

class BaseA : virtual public Base
{
public:
    int m_dataA;
    BaseA(){};
    ~BaseA(){};
};

class BaseB : virtual public Base
{
public:
    int m_dataB;
    BaseB(){};
    ~BaseB(){};
};

class Data : public BaseA, public BaseB
{
public:
    Data(){};
    ~Data(){};
};

int main()
{
    Data data;
    data.BaseA::m_data = 10;
    data.BaseB::m_data = 20;
    //通过虚继承,公共的基类数据就只有一份,加不加作用域都可以访问,三种方式结果都是20
    cout << data.m_data << endl;
    cout << data.BaseA::m_data << endl;
    cout << data.BaseB::m_data << endl;
    system("pause");
    return 0;
}

3.8 多态

通过子类重写父类虚函数构成多态,在这里注意与前面的函数重载,函数覆盖区别开。多态是发生在继承关系中的,必须重写虚函数,而重写的要求是函数返回值类型、函数名、参数列表、完全一致称为重写。

class Person
{
public:
    //纯虚函数,所以Person为抽象类,不可直接实例化
    virtual void printSex() = 0;
};

class Girl : public Person
{
public:
    void printSex()
    {
        cout << "gender is female" << endl;
    }
};

class Boy : public Person
{
public:
    void printSex()
    {
        cout << "gender is male" << endl;
    }
};

int main()
{
    Person *p = new Girl;
    //通过父类指针或者引用指向子类对象,发生多态
    p->printSex();
    Person *p1 = new Boy;
    return 0;
}

3.9 虚析构和纯虚析构

为什么要有虚析构存在呢?
在上面的示例中,如果我们释放p,那么只会调用Person的析构函数,而如果Girl中有一些操作在堆区中开辟了空间,由于释放p没有调用Girl的析构,将无法释放内存。
于是有了虚析构的做法,把析构函数携程虚函数,那么在调用父类析构时,就会先调用子类析构,再调用父类析构。
纯虚析构就是在虚析构的基础上,析构函数做空实现。区别就只在于包含了纯虚函数的类编程抽象类。

class Person
{
public:
    virtual void printSex() = 0;
    virtual ~Person() { cout << "调用Person析构" << endl; };
};

class Girl : public Person
{
public:
    void printSex()
    {
        cout << "gender is female" << endl;
    }
    ~Girl() { cout << "调用Girl析构" << endl; };
};

class Boy : public Person
{
public:
    void printSex()
    {
        cout << "gender is male" << endl;
    }
    ~Boy() { cout << "调用Boy析构" << endl; };
};

int main()
{
    Person *p = new Girl;
    p->printSex();
    delete p;
    system("pause");
    return 0;
}

输出以下内容:

gender is female
调用Girl析构
调用Person析构

4. 泛型编程

泛型编程的核心思想在于模板的使用,函数模板和类模板。本章主要对函数模板和类模板的使用做简单介绍,STL标准库是建立在模板的基础之上对常用操作形成的一套标准库。

4.1 函数模板

  • 函数模板在使用时可以不指定模板参数,自动推导
  • 如果是自动推导的话,不会进行隐式类型转换,推导不出来正确的参数类型时,无法调用函数模板
  • 函数模板在使用时也可以指定模板参数,指定参数的情况下能够发生参数的隐式类型转换
template <typename T>
T myMax(T a, T b)
{
    return a > b ? a : b;
}

int main()
{
    int a = 10;
    int b = 20;
    double c = 30;
    cout << "max is: " << myMax(a, b) << endl;
    cout << "max is: " << myMax<int>(a, c) << endl;
    system("pause");
    return 0;
}

函数模板能够发生重载

template <typename T>
T myMax(T a, T b)
{
    return a > b ? a : b;
}

template <typename T>
T myMax(T a, T b, T c)
{
    return max(max(a, b), c);
}

int main()
{
    int a = 10;
    int b = 20;
    double c = 30;
    cout << "max is: " << myMax<int>(a, b, c) << endl;
    system("pause");
    return 0;
}

4.2 类模板

template <typename nameType, typename ageType>
class Person
{
public:
    nameType m_name;
    ageType m_age;
    Person(nameType name, ageType age) : m_name(name), m_age(age){};
};

ostream &
operator<<(ostream &os, const Person<string, int> &p)
{
    os << p.m_name << " " << p.m_age << endl;
}

int main()
{
    Person<string, int> p("zhangsan", 28);
    cout << p << endl;
    system("pause");
    return 0;
}

类模板使用时必须指定模板参数,不可进行类型自动推导

//不可这样使用
Person p("zhangsan", 28);

//可以指定默认类型
template <typename nameType, typename ageType = int>
Person<string> p("zhangsan", 28);

//和函数默认参数使用方法一致,从左至右,从有默认参数的位置开始,后面的参数必须有默认参数
//错误用法
template <typename nameType = string, typename ageType>

参数模板化

上面的案例中重载<<运算符时,在传入参数时指定了模板的参数类型。另外一种方法时直接将类的参数也编程模板,即把这个函数变成一个模板

template <typename nameType, typename ageType>
class Person
{
public:
    nameType m_name;
    ageType m_age;
    Person(nameType name, ageType age) : m_name(name), m_age(age){};
};

template <typename nameType, typename ageType>
ostream &
operator<<(ostream &os, const Person<nameType, ageType> &p)
{
    os << p.m_name << " " << p.m_age << endl;
}

int main()
{
    //实例化对象的啥时候制定了模板参数类型,调用函数时不需要再指定函数的模板参数类型
    Person<string, int> p("zhangsan", 28);
    cout << p << endl;
    system("pause");
    return 0;
}

4.3 类模板继承

基类是类模板时,子类在继承时必须指定模板参数列表:

template <typename nameType, typename ageType>
class Person
{
public:
    nameType m_name;
    ageType m_age;
    Person(nameType name, ageType age) : m_name(name), m_age(age){};
};

ostream &
operator<<(ostream &os, const Person<string, int> &p)
{
    os << p.m_name << " " << p.m_age << endl;
    return os;
}

class Employee : public Person<string, int>
{
public:
    Employee(string name, int age) : Person(name, age){};
};

int main()
{
    Employee p("zhangsan", 28);
    cout << p << endl;
    system("pause");
    return 0;
}

4.4 类模板中成员函数类外实现

template <typename nameType, typename ageType>
class Person
{
public:
    nameType m_name;
    ageType m_age;
    Person(nameType name, ageType age) : m_name(name), m_age(age){};
    void printInfo();
};

//类外实现成员函数时,引用Person::时必须有模板参数列表,因此需要将函数变为模板
template <typename nameType, typename ageType>
void Person<nameType, ageType>::printInfo()
{
    cout << m_name << " " << m_age << endl;
}

int main()
{
    Person<string, int> p("zhangsan", 28);
    p.printInfo();
    system("pause");
    return 0;
}

5. STL标准库

5.1 STL四大组件

STL是充分利用C++的语言特性,大量的使用模板,将常用的数据结构和算法融合在一起的标准库。使用STL能够简化程序中对于数据结构和算法的实现。
STL中包含常用的四大组件:容器、算法、迭代器、仿函数。

5.2 STL简单使用示例

这里通过一个小的示例展示STL的基本使用,包含容器、迭代器和算法。

template <typename nameType, typename ageType>
class Person
{
public:
    nameType m_name;
    ageType m_age;
    Person(nameType name, ageType age) : m_name(name), m_age(age){};
    void printInfo();
    bool operator==(const Person &p);
};
template <typename nameType, typename ageType>
bool Person<nameType, ageType>::operator==(const Person &p)
{
    if ((this->m_name == p.m_name) && (this->m_age == p.m_age))
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

template <typename nameType, typename ageType>
void Person<nameType, ageType>::printInfo()
{
    cout << m_name << " " << m_age << endl;
}

int main()
{
    Person<string, int> p1("张三", 28);
    Person<string, int> p2("李四", 35);
    Person<string, int> p3("王五", 25);
    Person<string, int> p4("赵六", 22);

    vector<Person<string, int>> group;
    group.push_back(p1);
    group.push_back(p2);
    group.push_back(p3);
    group.push_back(p4);

    cout << "组员列表:" << endl;
    for (vector<Person<string, int>>::iterator it = group.begin(); it != group.end(); it++)
    {
        it->printInfo();
    }

    cout << "查找一个28岁的张三:" << endl;
    vector<Person<string, int>>::iterator it = find(group.begin(), group.end(), Person<string, int>("张三", 28));

    if (it == group.end())
    {
        cout << "查无此人" << endl;
    }
    else
    {
        cout << "找到:" << it->m_name << " " << it->m_age << endl;
    }

    system("pause");
    return 0;
}

5.3 string容器

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

// system("chcp 65001");

int main()
{
    string name = "jack";
    //拷贝构造
    string addr("gmail.com");
    string link("@");
    //字符串拼接
    string info = name + link + addr;
    cout << info << endl;

    //字符串元素存取,索引从0开始
    cout << info[4] << endl;
    cout << info.at(4) << endl;

    //字符串中查找字符
    int pos = info.find("@");

    //裁剪子字符串,从0位置开始,取pos个字符
    string username = info.substr(0, pos);
    cout << "user name:" << username << endl;

    string info2(info);
    //从0到pos之间的字符替换为"jie"
    info2.replace(0, pos, "jie");
    cout << info2 << endl;

    //字符串比较,compare相同时返回0,也可直接"=="
    if (!info.compare(info2))
    // if (info == info2)
    {
        cout << "邮箱地址相同" << endl;
    }
    else
    {
        cout << "邮箱地址不同" << endl;
    }

    system("pause");
    return 0;
}

5.4 vector容器

vector是单端数组,是连续的存储空间,因此可以像数组一样通过指针的偏移连续的访问vector中的元素。
vector的内存空间是根据实际的大小进行动态分配的。

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

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

int main()
{
    vector<int> a;
    //尾部插入元素
    a.push_back(1);
    a.push_back(2);
    a.push_back(3);
    a.push_back(4);

    //拷贝构造
    vector<int> b(a);
    //尾部删除
    b.pop_back();

    //结合算法for_each使用
    for_each(b.begin(), b.end(), printVal);

    //访问数据,数据存取
    cout << b[1] << "  " << b.at(1) << endl;
    b.at(1) = 10;
    b[1] = 20;

    //获取vector的元素个数
    cout << "b的大小为:" << b.size() << endl;
    //获取vector的容量
    cout << "b的容量为:" << b.capacity() << endl;

    b.push_back(5);
    b.push_back(6);
    b.push_back(7);

    //结合迭代器使用
    for (vector<int>::iterator it = b.begin(); it != b.end(); it++)
    {
        cout << *it << endl;
    }

    cout << "b的大小为:" << b.size() << endl;
    cout << "b的容量为:" << b.capacity() << endl;

    //修改容器的大小
    b.resize(10);
    cout << "b的大小为:" << b.size() << endl;

    system("pause");
    return 0;
}

5.5 list容器

list容器是一种链式存储结构,其内存物理空间是不连续的
list容器的优点是进行插入和删除操作时,只需要修改指针,不需要移动元素

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

// system("chcp 65001");

int main()
{
    list<string> nameList;
    nameList.push_back("张三");
    nameList.push_back("李四");
    nameList.push_back("王五");
    nameList.push_front("赵六");
    nameList.remove("李四");

    for (list<string>::iterator it = nameList.begin(); it != nameList.end(); it++)
    {
        cout << (*it) << endl;
    }

    list<string>::iterator itn = nameList.begin();

    // itn = itn + 1; 内存不连续,不支持这样的跳跃访问

    cout << "nameList 的大小:" << nameList.size() << endl;
    nameList.clear();
    cout << "nameList 的大小:" << nameList.size() << endl;
    system("pause");
    return 0;
}

5.6 set容器

set/multiset属于关联式容器,底层结构是用二叉树实现。所以不再有头插和尾插这种线性数据的操作。
set中的元素不可以有重复,插入到set中的元素会自动进行排序。

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


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

int main()
{
    set<int> score;
    score.insert(10);
    score.insert(50);
    score.insert(20);
    score.insert(30);

    // 默认从小到大的顺序排序
    for (set<int>::iterator it = score.begin(); it != score.end(); it++)
    {
        cout << (*it) << endl;
    }

    // 利用仿函数可以改变排序方法
    set<int, myCompare> score2;
    score2.insert(10);
    score2.insert(50);
    score2.insert(20);
    score2.insert(30);

    for (set<int>::iterator it = score2.begin(); it != score2.end(); it++)
    {
        cout << (*it) << endl;
    }
    system("pause");
    return 0;
}

5.7 pair的基本使用

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


//为定义的pair<string,int>起一个别名 personInfo,方便其频繁使用
typedef pair<string, int> personInfo;

//通过仿函数实现自定义排序方法
class myCompare
{
public:
    bool operator()(personInfo p1, personInfo p2)
    {
        if (p1.second < p2.second)
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }
};

int main()
{
    // pair是一种数据类型模板,将两个数据组合在一起变成一个数据,通过first和second访问
    pair<string, int> person;
    person.first = "jack";
    person.second = 28;
    cout << person.first << " " << person.second << endl;

    //set中存放自定义容器时,要自定义排序方法
    set<personInfo, myCompare> group;
    group.insert(personInfo("zhangsan", 15));
    group.insert(personInfo("lisi", 30));
    group.insert(personInfo("xiaoliu", 8));
    for (set<personInfo, myCompare>::iterator it = group.begin(); it != group.end(); it++)
    {
        cout << it->first << " " << it->second << endl;
    }

    system("pause");
    return 0;
}

5.8 map容器

map/multimap属于关联式容器,底层结构是用二叉树实现。
其中存放的数据都是pair类型的数据,pair的第一个值为key值,第二个值为value
其中的元素根据key值自动排序

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

int main()
{
    map<int, string> info;
    info.insert(pair<int, string>(1, "zhangsan"));
    info.insert(make_pair(3, "lisi"));
    info.insert(make_pair(2, "xiaoliu"));
    //默认按照key值从小到大排序
    for (map<int, string>::iterator it = info.begin(); it != info.end(); it++)
    {
        cout << it->first << " " << it->second << endl;
    }

    system("pause");
    return 0;
}
1 zhangsan
2 xiaoliu
3 lisi
Press any key to continue . . .

使用仿函数自定义排序方式,注意仿函数的参数为key值得类型

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


class myCompare
{
public:
    // key值为int类型
    bool operator()(int p1, int p2)
    {
        return p1 > p2;
    }
};

int main()
{
    map<int, string, myCompare> info;
    info.insert(pair<int, string>(1, "zhangsan"));
    info.insert(make_pair(3, "lisi"));
    info.insert(make_pair(2, "xiaoliu"));

    for (map<int, string, myCompare>::iterator it = info.begin(); it != info.end(); it++)
    {
        cout << it->first << " " << it->second << endl;
    }

    system("pause");
    return 0;
}

5.9 仿函数

仿函数是一个类,不是一个函数。
这个类是重载函数操作符,用这个类创建的对象称为函数对象。
函数对象可以作为函数参数传递

class funMo
{
public:
    int operator()(int a, int b)
    {
        cout << "sum is: " << (a + b) << endl;
        return a + b;
    }
};

void doAdd(funMo &fun, int a, int b)
{
    fun(a, b);
}

int main()
{
    funMo funObj;
    // 重载函数操作符的类,实例化的函数对象,可以像函数一样使用,所以成为仿函数
    funObj(1, 2);

    // 函数对象可以作为函数参数使用
    doAdd(funObj, 3, 4);

    system("pause");
    return 0;
}

5.10 谓词

返回bool类型的仿函数称为谓词,我们在前面的示例中自定义排序方式使用的就是谓词;

仿函数中只有一个输入参数时一元谓词,有两个参数时二元谓词,我们前面使用的都是二元谓词;

这里举例一个一元谓词:

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

// system("chcp 65001");

class funMo
{
public:
    bool operator()(int a)
    {
        return a > 18;
    }
};

int main()
{
    set<int> score;
    score.insert(10);
    score.insert(18);
    score.insert(23);

    //注意这里调用的是funMo(),其实是一个匿名对象,不能直接用类名作为函数参数
    set<int>::iterator pos = find_if(score.begin(), score.end(), funMo());

    cout << *pos << endl;

    system("pause");
    return 0;
}

5.11 常用算法–查找

find查找指定元素,返回其迭代器,如果没有找到,返回end()迭代器

    set<int>::iterator pos = find(score.begin(), score.end(), 10);
    cout << *pos << endl;

find_if按条件查找,条件可以通过仿函数自定义一元谓词

set<int>::iterator pos = find_if(score.begin(), score.end(), funMo());

5.12 常用算法–排序

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

// system("chcp 65001");

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

class funPrint
{
public:
   void operator()(int val)
   {
       cout << val << endl;
   }
};

int main()
{
   vector<int> score;
   score.push_back(10);
   score.push_back(20);
   score.push_back(30);
   score.push_back(40);

   //自定义函数,要放函数名(指针)
   for_each(score.begin(), score.end(), myPrint);

   //排序算法,使用内建的关系仿函数
   sort(score.begin(), score.end(), greater<int>());

   //使用仿函数,要放函数对象(不能是类名)
   for_each(score.begin(), score.end(), funPrint());

   system("pause");

   return 0;
}

}
};

int main()
{
set score;
score.insert(10);
score.insert(18);
score.insert(23);

//注意这里调用的是funMo(),其实是一个匿名对象,不能直接用类名作为函数参数
set<int>::iterator pos = find_if(score.begin(), score.end(), funMo());

cout << *pos << endl;

system("pause");
return 0;

}


### 5.11 常用算法--查找
find查找指定元素,返回其迭代器,如果没有找到,返回end()迭代器
```c++
    set<int>::iterator pos = find(score.begin(), score.end(), 10);
    cout << *pos << endl;

find_if按条件查找,条件可以通过仿函数自定义一元谓词

set<int>::iterator pos = find_if(score.begin(), score.end(), funMo());

5.12 常用算法–排序

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

// system("chcp 65001");

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

class funPrint
{
public:
   void operator()(int val)
   {
       cout << val << endl;
   }
};

int main()
{
   vector<int> score;
   score.push_back(10);
   score.push_back(20);
   score.push_back(30);
   score.push_back(40);

   //自定义函数,要放函数名(指针)
   for_each(score.begin(), score.end(), myPrint);

   //排序算法,使用内建的关系仿函数
   sort(score.begin(), score.end(), greater<int>());

   //使用仿函数,要放函数对象(不能是类名)
   for_each(score.begin(), score.end(), funPrint());

   system("pause");

   return 0;
}