【C++】入门笔记

167 阅读24分钟

基础

int arr[] = {1,2,3};
cout << arr;//输出数组地址
cout << arr[0];//与行2相等

int* p = arr;
p++;//此时指针移动四个字节(int大小)
int arr[2][3] = {{},{}};
num1 = sizeof(arr)/sizeof(arr[0]);//求行数
num2 = sizeof(arr[0])/sizeof(arr[0][0]);//求列数

  • 值传递:调用有参函数时,实参将数值传给形参,而形参的改变无法影响实参!
mindmap
      各自开辟不同地址
          a
          b
          num1
          num2
          temp
    
void swap1(int n1,int n2) {
	int temp = n1;
	n1 = n2;
	n2 = temp;
	cout << n1 << " " << n2 << endl;
}

int main() {
	int a = 10;
	int b = 20;
	swap1(a, b);//输出20 10
	cout << a << endl;//输出10
	cout << b << endl;//输出20
	system("pause");
	return 0;
}
  • 指针传递可改变实参
mindmap
      形参为指针,传入实参地址
          a
          b
          &a
          &b
          temp
    
int* const p = &a;//此时不可改变指针  *p=b √
const int* p = &a;//此时不可改变指针指向  p=&b √
const int* const p = &a;//均不可改
void bubblesort(int arr[], int len) {//与数组有关的函数一般都会传入数组长度
	for (int i = 0; i < len - 1; i++) { //len - 1表示len个数需要冒泡len-1轮,小于号是因为从0开始计数
		for (int j = 0; j < len - 1 - i; j++) {//每轮i的结果为最小的数放到最后
			int temp = 0;
			if (*(arr + j) < *(arr + j + 1)) {
				temp = *(arr + j + 1);
				*(arr + j + 1) = *(arr + j);
				*(arr + j) = temp;
			}//每轮j都将比较两个相邻的数并将小的数往后放
			/*if (arr[j] < arr[j + 1]) {
				temp = arr[j + 1];
				arr[j + 1] = arr[j];
				arr[j] = temp;
			}*/
		}
	}
}
struct student {
    string name;
    int score;
}
student a = {"张三",100};
student* p = &a;
//对象访问属性用"." 指针访问属性用"->"
cout << a.score << " " << p->score;//输出100 100

程序的内存模型

  • 代码区:存放二进制的机器指令,由写的代码转换
  • 全局区:存放全局变量(定义在函数外的变量)、静态变量(函数中定义:static int a)、常量
  • 栈区:存放函数形参和局部变量,在函数执行完后由编译器自动释放,因此不可返回局部变量的地址
int* func(){
    int a = 10;//局部变量,存放在栈区
    return &a;
}
int main(){
    int* p = func();
    cout << *p << endl;//输出10,即编译器保留一次栈区数据
    cout << *P << endl;//输出1158779630,即此时编译器已释放栈区数据
    system("pause");return 0;
}
  • 堆区:由程序员分配、释放,若程序运行期间未释放,则程序结束后关闭时由操作系统释放
int* func() {
	int* a = new int(10);int* arr = new int[10];//在堆区开辟空间
	return a;
}
int main() {
	int* p = func();
	cout << *p << endl;
	delete p;delete[] arr;//手动释放空间
	cout << *p << endl;//已释放空间,再进行操作则会报错
	system("pause"); return 0;
}

引用

int a = 10;
int &b = a;
cout << &a << endl;
cout << &b << endl;
输出结果:a与b的地址相同,即a和b为同一物
int &a;//报错
int &b = a;
int &b = c;//报错
void swap1(int& a, int& b) {//传入引用
	int temp = a;
	a = b;
	b = temp;
}
int main() {
	int n1 = 10;
	int n2 = 20;
	swap1(n1, n2);
	cout << n1 << " " << n2 << endl;//输出20 10
	system("pause");
	return 0;
}

函数高级

void func(int a,int){
...}

-函数重载条件:①同一作用域 ②函数名称相同 ③形参类型不同/形参个数不同/形参顺序不同

void func(int a) {
	cout << "1" << endl;
}

void func(double b) {
	cout << "2" << endl;
}

void func(int a, double b) {
	cout << "3" << endl;
}

void func(double b, int a) {
	cout << "4" << endl;
}

int main() {
	func(10);//输出1
	func(10.0);//输出2
	func(10, 10.0);//输出3
	func(10.0, 10);//输出4
	system("pause");return 0;
}

类和对象

  • 面对对象编程语言三大特性:封装、继承、多态

封装

①将属性和行为作为整体来表现事物 ②权限控制

  • Public 类内、外均可访问
  • Protected 类内可访问,类外不可访问
  • private 类内可访问,类外不可访问
class Person
{
public:
	Person()//创建对象时调用构造函数
	{
		cout << "Person的构造函数调用" << endl;
	}
	~Person()//销毁对象时调用析构函数
	{
		cout << "Person的析构函数调用" << endl;
	}
        Person(int a, int b, int c) :m_a(a), m_b(b), m_c(c) {}//简易构造方法
};

void test01()
{	
	cout << "全局函数中:" << endl;
	Person p1;
}

int main() {
	test01();
	cout << "main函数中:" << endl;
	Person p;
	system("pause");
	return 0;
}
输出结果:全局函数中的构造与析构均被调用,main函数中只调用构造函数(主函数中的局部变量放在栈区,
只有在函数执行完才被销毁,而主函数只有在窗口被关闭后才执行完)
  • 默认拷贝函数只提供简单的浅拷贝(值拷贝),当属性在堆区开辟时,浅拷贝会导致重复释放并报错

微信图片_20230905112102.png

class Person {
public:
	Person(int age, int height) {
		m_age = age;
		m_height = new int(height);//初始化时传入int,在构造函数内转化为指针。若为浅拷贝,则直接
拷贝该处指针,导致两个对象析构时重复释放该指针指向区域

	}
	Person(const Person& p) {
		m_age = p.m_age;
		m_height = new int(*p.m_height);//深拷贝时,在堆区开辟新的区域,则不会重复释放

	}
	~Person() {
		if (m_height != NULL)//指针不为空时,释放指针指向区域
		{
			delete m_height;//手动释放堆区
		}
	}
public:
	int m_age;
	int* m_height;//成员属性为指针
};
class Person
{
public:
	static int m_A; //静态成员变量,类内声明,不占用类的空间
private:
	static int m_B; 
};
int Person::m_A = 10;//类外初始化
int Person::m_B = 10;

void test01()
{
	//静态成员变量两种访问方式
	//1、通过对象
	Person p1;
	p1.m_A = 100;
	cout << "p1.m_A = " << p1.m_A << endl;//输出:100

	Person p2;
	p2.m_A = 200;
	cout << "p1.m_A = " << p1.m_A << endl; //输出:200
	cout << "p2.m_A = " << p2.m_A << endl;//输出:200
  //静态成员即为公用共享成员
  //静态成员函数只能访问静态成员变量
  
	//2、通过类名
	cout << "m_A = " << Person::m_A << endl;
	//cout << "m_B = " << Person::m_B << endl; //私有权限访问不到
}
class person {
public:
	person(int age):age(age){}
	person& personadd(person p1) {//用&接收才为自身而非拷贝
		this->age += p1.age;//this指针指向当前类实例化的对象,如p1,p2
		return *this;//返回类型为person&,即返回实例对象自身
	}
public:
	int age;
};

int main() {
	person p1(10);
	person p2(20);
	p1.personadd(p2).personadd(p2);//10+20+20
	cout << p1.age << endl;//输出50
	system("pause");
	return 0;
}
  • 全局函数做友元:在类内声明全局函数并前缀friend:friend void test01();
  • 类做友元:friend class test02;
  • 其他类的成员函数做友元:friend void test02::test01();

运算符重载

  • 用于实现自定义数据类型的运算,重载的本质为函数
class Person {
public:
	Person(int a, int b): m_A(a), m_B(b) {}
        
	//成员函数实现 + 号运算符重载
	Person operator+(const Person& p) {//返回person才可用person对象接收
		Person temp;
		temp.m_A = this->m_A + p.m_A;
		temp.m_B = this->m_B + p.m_B;
		return temp;
	}
public:
	int m_A;
	int m_B;
};

//全局函数实现 + 号运算符重载
//Person operator+(const Person& p1, const Person& p2) {//operator+即为函数名
//	Person temp(0, 0);
//	temp.m_A = p1.m_A + p2.m_A;
//	temp.m_B = p1.m_B + p2.m_B;
//	return temp;
//}

//运算符重载 可以发生函数重载 
Person operator+(const Person& p2, int val)//形参不同的重载  
{
	Person temp;
	temp.m_A = p2.m_A + val;
	temp.m_B = p2.m_B + val;
	return temp;
}

void test() {

	Person p1(10, 10);
	Person p2(20, 20);

	Person p3 = p2 + p1;  //相当于 p2.operator+(p1)或operator+(p1,p2)
	cout << "mA:" << p3.m_A << " mB:" << p3.m_B << endl;

	Person p4 = p3 + 10; //相当于 operator+(p3,10)
	cout << "mA:" << p4.m_A << " mB:" << p4.m_B << endl;
}

int main() {
	test();
	system("pause");
	return 0;
}
class Person {
public:
	Person(int a, int b): m_A(a), m_B(b) {}
	//若用成员函数重载,则本质为p.operator<<(const ostream& cout),简化为p<<cout,不符常规
public:
	int m_A;
	int m_B;
};

//全局函数实现左移重载
ostream& operator<<(ostream& out, Person& p) {//ostream为类名,用于输出,out为形参
	out << "a:" << p.m_A << " b:" << p.m_B;
	return out;//返回输出类,以便链式编程
}

void test() {
	Person p1(10, 20);
	cout << p1 << "hello world" << endl; //链式编程,本质为operator<<(cout,p1)
}

int main() {
	test();
	system("pause");
	return 0;
}
class person {
public:
	person(int a) :age(a){}
        //重载前置++
	person& operator++() {返回自身以便链式编程
		++age;
		return *this;
	}
        //重载后置++且由于函数名相同,同时重载函数
	person operator++(int) {//只能返回值,因为temp为临时变量存放在栈区,程序结束即释放,不能返回
		person temp = *this;
		age++;
		return temp;
	}
	
public:
	int age;
};

//全局函数重载
person& operator++(person& p) {
	p.age++;
	return p;
}

person operator++(person& p,int) {//operator++函数重载
	person temp = p;
	p.age++;
	return temp;
}

int main() {
	person p1(10);
	person p2(10);
	cout << (++p1).age << endl;//输出11
	cout << p1.age << endl;//输出11
	cout << (p2++).age << endl;//输出10
	cout << p2.age << endl;//输出11
	system("pause");
	return 0;
}

继承

  • class 子类: 继承方式 父类, 继承方式 父类 {}
  • 继承方式:

微信图片_20230922160706.jpg

  • 创建子类对象时,会先创造父类,后构造子类;析构时相反
  • 当父类与子类有同名成员属性或成员函数时,用son.m_A或son.func()访问子类,用son.base::m_A或son.base::func()访问父类
  • 虚继承(virtual)可解决菱形继承(两个子类均继承同一父类,且第三个子类同时继承这两个子类)时出现的多份同名数据问题:
class son1: virtual public base {} 
class son2: virtual public base {}
class son3: public son1, public son2 {}

多态

  • 静态多态:函数重载和运算符重载
  • 动态多态:函数地址晚绑定,在运行阶段才确定函数地址
class Animal
{
public:
//函数前面加上virtual关键字,变成虚函数,那么编译器在编译的时候就不能确定函数调用了
	virtual void speak()
	{
		cout << "动物在说话" << endl;
	}
};

class Cat :public Animal
{
public:
	void speak()
	{
		cout << "小猫在说话" << endl;
	}
};

class Dog :public Animal
{
public:
	void speak()
	{
		cout << "小狗在说话" << endl;
	}

};

void DoSpeak(Animal& animal)//形参为父类指针或引用
{
	animal.speak();
}
//
//多态满足条件: 
//1、有继承关系
//2、子类重写父类中的虚函数,重写即函数名、返回值、形参都一样,区别重载
//多态使用:
//父类指针或引用指向子类对象

void test01()
{
	Cat cat;
	DoSpeak(cat);//父类指针或引用指向子类对象


	Dog dog;
	DoSpeak(dog);//父类指针或引用指向子类对象
}


int main() {

	test01();
	system("pause");
	return 0;
}
class AbstractCalculator
{
public:
	virtual int getResult() = 0;//计算函数为纯虚函数
public:
	int m_Num1;
	int m_Num2;
};

//加法计算器
class AddCalculator :public AbstractCalculator
{
public:
	int getResult()//重写父类虚函数
	{
		return m_Num1 + m_Num2;
	}
};

//减法计算器
class SubCalculator :public AbstractCalculator
{
public:
	int getResult()//重写父类虚函数
	{
		return m_Num1 - m_Num2;
	}
};

int main() {
	//创建加法计算器
	AbstractCalculator* abc = new AddCalculator;//父类指针指向子类对象
	abc->m_Num1 = 10;
	abc->m_Num2 = 10;
	cout << abc->getResult() << endl;//输出20;即使是披着父类的“外壳”的指针abc,但由于指向了子类的对象,从而发生了多态,执行子类内容
	delete abc;  //用完了记得销毁

	//创建减法计算器
	abc = new SubCalculator;//父类指针指向子类对象
	abc->m_Num1 = 10;
	abc->m_Num2 = 10;
	cout << abc->getResult() << endl;//输出0
	delete abc;

	system("pause");
	return 0;
}
//抽象制作饮品
class AbstractDrinking {
public:
	virtual void Boil() = 0;
	virtual void Brew() = 0;
	virtual void PourInCup() = 0;
	virtual void PutSomething() = 0;
        
	void MakeDrink() {
		Boil();
		Brew();
		PourInCup();
		PutSomething();
	}
};

//制作咖啡
class Coffee : public AbstractDrinking {
public:
	void Boil() {
		cout << "煮农夫山泉!" << endl;
	}
	void Brew() {
		cout << "冲泡咖啡!" << endl;
	}
	void PourInCup() {
		cout << "将咖啡倒入杯中!" << endl;
	}
	void PutSomething() {
		cout << "加入牛奶!" << endl;
	}
};

//制作茶水
class Tea : public AbstractDrinking {
public:
	void Boil() {
		cout << "煮自来水!" << endl;
	}
	void Brew() {
		cout << "冲泡茶叶!" << endl;
	}
	void PourInCup() {
		cout << "将茶水倒入杯中!" << endl;
	}
	void PutSomething() {
		cout << "加入枸杞!" << endl;
	}
};

void DoWork1(AbstractDrinking* drink) {
	drink->MakeDrink();
	delete drink;//记得手动释放堆区数据
}

void Dowork2(AbstractDrinking& drink) {//传入引用,无需释放,但需要实例化子类对象
        drink.MakeDrink();
}

void test01() {
	DoWork1(new Coffee);
	DoWork1(new Tea);//父类指针指向子类对象
}

void test02() {
        Coffee co;
        Tea te;
        Dowork2(co);
        Dowork2(te);//父类引用指向子类对象
}

模板

函数模板

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

//普通函数模板
template<typename T>
bool myCompare(T& a, T& b)
{
	if (a == b)
	{
		return true;
	}
	else
	{
		return false;
	}
}


//具体化,显示具体化的原型和定意思以template<>开头,并通过名称来指出类型
//相当于为前面的myCompare模板给出特殊情况时的解释
template<> bool myCompare(Person& p1, Person& p2)
{
	if ( p1.m_Name  == p2.m_Name && p1.m_Age == p2.m_Age)
	{
		return true;
	}
	else
	{
		return false;
	}
}

void test01()
{
	int a = 10;
	int b = 20;
	//内置数据类型可以直接使用通用的函数模板
	bool ret = myCompare(a, b);
	if (ret)
	{
		cout << "a == b " << endl;
	}
	else
	{
		cout << "a != b " << endl;
	}
}

void test02()
{
	Person p1("Tom", 10);
	Person p2("Tom", 10);
	//自定义数据类型,不会调用普通的函数模板
	//可以创建具体化的Person数据类型的模板,用于特殊处理这个类型
	bool ret = myCompare(p1, p2);
	if (ret)
	{
		cout << "p1 == p2 " << endl;
	}
	else
	{
		cout << "p1 != p2 " << endl;
	}
}

类模板

#include <string>
//类模板
template<class NameType, class AgeType> 
class Person
{
public:
	Person(NameType name, AgeType age)
	{
		this->mName = name;
		this->mAge = age;
	}
	void showPerson()
	{
		cout << "name: " << this->mName << " age: " << this->mAge << endl;
	}
public:
	NameType mName;
	AgeType mAge;
};

void test01()
{
	// 指定NameType 为string类型,AgeType 为 int类型
	Person<string, int>P1("孙悟空", 999);
	P1.showPerson();
}

STL

  • standard template library 标准模板库
  • STL 从广义上分为: 容器(container) 算法(algorithm) 迭代器(iterator)
  • 容器和算法之间通过迭代器进行无缝连接
  • 序列式容器:强调值的排序,序列式容器中的每个元素均有固定的位置。
    关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系
  • 质变算法:是指运算过程中会更改区间内的元素的内容。例如拷贝,替换,删除等等
    非质变算法:是指运算过程中不会更改区间内的元素内容,例如查找、计数、遍历、寻找极值等等

string

#include <string>
int main(){
    string s1; //创建空字符串,调用无参构造函数
    cout << "str1 = " << s1 << endl;

    const char* str = "hello world";
    string s2(str); //把c_string转换成了string

    cout << "str2 = " << s2 << endl;

    string s3(s2); //调用拷贝构造函数
    cout << "str3 = " << s3 << endl;

    string s4(10, 'a');
    cout << "str4 = " << s4 << endl;//输出aaaaaaaaaa
}
}
#include <string>
int main(){
    string s1 = "abc";//a为第0位,以此类推
    int pos = s1.find("bc");//find为string类的成员函数,返回查到的位置
    cout << pos;//输出1
    int pos = s1.find("ac");
    cout << pos;//输出-1,意为查不到
}
#include <string>
int main(){
    string str1 = "abcdefgde";//b为1号位
    str1.replace(1, 3, "1111");//从1号位开始替换,将1号位开始的3个字符(bcd)替换为1111
    cout << "str1 = " << str1 << endl;//输出a1111efgde
}
#include <string>
int main(){
    string str = "hello";//e为1号位
    str.insert(1, "111");//在1号位前插入111
    cout << str << endl;//输出h111ello

    str.erase(1, 3);  //从1号位置开始删除3个字符
    cout << str << endl;//输出hello
}
#include <string>
int main(){
    string str = "abcdefg";//b为1号位
    string subStr = str.substr(1, 3);//从1号位开始截取3个字符
    cout << "subStr = " << subStr << endl;//输出bcd
}

vector

  • 可看作单端数组,区别是数组为静态空间,vector为动态拓展
  • 动态拓展:并不是在原空间之后续接新空间,而是找更大的内存空间,然后将原数据拷贝新空间,释放原空间
#include <vector>

void printVector(vector<int>& v) {// 形参为vector<int>本身
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {//vector<int>为类,iterator理解为该类中
   的成员属性,为迭代器,理解为指针,it即为iterator实例化,v.begin()指向容器首个元素,v.end()指向容器末尾元素的
   下一个元素。
		cout << *it << " ";//it为指针,用*取得指针指向的内容
	}
	cout << endl;
}

void test01()
{
	vector<int> v1; //无参构造
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
	}

	vector<int> v2(v1.begin(), v1.end());//将v1容器的元素搬到v2中

	vector<int> v3(10, 100);//得到10个100的v3容器
	
	vector<int> v4(v3);//拷贝
}
v1.capacity();//得到容器总容量
v1.size();//得到容器大小,即当前元素个数
v1.resize(新的size,填充元素);//改变容器大小,且用填充元素填充多出来的部分
v1.reszie(新的size);//改变容器大小,且删去后面多出来的部分元素
v1.push_back(num);//将num加入到v1末尾
v1.pop_back();//将v1最后一个元素删掉
v1.insert(指示位置的迭代器,插入个数,插入元素);//在迭代器位置前插入几个元素,重新找新的区域来放置插入后的容器!
v1.erase(指示位置的迭代器);//删除该位元素
v1.erase(迭代器a,迭代器b);//删除从a到b的元素
v1[pos];//得到pos处的元素
v1.front();//得到v1首元素
v1.back();//得到v1尾元素
v1.swap(v2);//完全交换两个容器,包括数据、容量、大小
v1.reserve(num);//为容器预留num的容量,可以减少vector动态拓展的次数,用于大数据量时使用

deque

  • 双端数组,无容量capacity概念
  • 在中控区记录缓冲区地址,数据放在缓冲区: 微信截图_20230930112403.png
  • 构造、大小部分与vector一样
d1.push_back();d1.pop_back();
d1.push_front(num);//在首位添加num
d1.pop_front();//删去首位数据
d1.insert(指示位置的迭代器,插入个数,插入元素);
d1.insert(指定位置的迭代器,d2迭代器a,d2迭代器b);//在指定位置前插入d2容器中a到b的元素
erase同vector
d1[pos];
d1.front();
d1.back();

stack

微信截图_20230930112403.png

  • 由于只能访问栈顶元素,因此stack无法遍历!
  • 先进后出!
s1.empty();
s1.size();
s1.push(num);//入栈
s1.pop();//出栈
s1.top();//访问栈顶元素

queue

微信截图_20230930112403.png

  • 只能访问队尾、队首,因此queue无法遍历!
  • 先进先出!
  • 与deque区别:queue相当于排头在右边的排队,deque相当于排头在左边的排队,但front()均指向首个元素
q1.empty();
q1.size();
q1.push(num);//在队末添加元素
q1.pop();//删去队首元素
q1.front();//访问队首元素
q1.back();//访问队末元素

list

微信截图_20230930112403.png

  • 链表由一系列结点组成
  • prev指向上一个结点,next指向下一个结点
  • 由于链表的存储方式并不是连续的内存空间,因此链表list中的迭代器只支持前移和后移,属于双向迭代器
  • 优点:可对任意位置进行快速的增删改,而数组型容器需要通过移动数据完成
  • 大多数成员函数与前面容器相同
l1.reverse();//反转链表
l1.sort();//由小到大排序
#include <list>
#include <string>
class Person {
public:
	Person(string name, int age , int height) {
		m_Name = name;
		m_Age = age;
		m_Height = height;
	}
public:
	string m_Name;  //姓名
	int m_Age;      //年龄
	int m_Height;   //身高
};

bool ComparePerson(Person& p1, Person& p2) {//排序规则,形参为自定义数据引用
	if (p1.m_Age == p2.m_Age) {
		return p1.m_Height  > p2.m_Height;//若年龄相同,则身高由大到小排序
	}
	else
	{
		return  p1.m_Age < p2.m_Age;//年龄由小到大排序
	}

}

void test01() {
	list<Person> L;

	Person p1("刘备", 35 , 175);
	Person p2("曹操", 45 , 180);
	Person p3("孙权", 40 , 170);
	Person p4("赵云", 25 , 190);
	Person p5("张飞", 35 , 160);
	Person p6("关羽", 35 , 200);

	L.push_back(p1);
	L.push_back(p2);
	L.push_back(p3);
	L.push_back(p4);
	L.push_back(p5);
	L.push_back(p6);

	for (list<Person>::iterator it = L.begin(); it != L.end(); it++) {
		cout << "姓名: " << it->m_Name << " 年龄: " << it->m_Age 
              << " 身高: " << it->m_Height << endl;
	}

	cout << "---------------------------------" << endl;
	L.sort(ComparePerson); //将前面的排序规则函数名传入sort函数,无括号

	for (list<Person>::iterator it = L.begin(); it != L.end(); it++) {
		cout << "姓名: " << it->m_Name << " 年龄: " << it->m_Age 
              << " 身高: " << it->m_Height << endl;
	}
}

set/multiset

  • set不允许容器中有重复元素,multiset允许容器中有重复元素
  • 在插入元素时,容器会自动排序,排序规则同链表,可自定
s.insert(num);
s.erase(迭代器);
s.erase(num);//删除num这个数
s.find(num);//查找num这个数,若找到了,则返回该数对应迭代器(为指针),反之返回s.end()
s.count(num);//统计num的数量
#include <set>
#include <string>

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

	string m_Name;
	int m_Age;

};
class comparePerson//与链表不同,此处为一个类,在类中写与链表自定排序规则相似的仿函数
{
public:
	bool operator()(const Person& p1, const Person &p2)//重载小括号,自定义数据类型作形参时注意const和&
	{
		//按照年龄进行排序  降序
		return p1.m_Age > p2.m_Age;
	}
};

void test01()
{
	set<Person, comparePerson> s;//构造时加上排序规则的类名,注意无括号

	Person p1("刘备", 23);
	Person p2("关羽", 27);
	Person p3("张飞", 25);
	Person p4("赵云", 21);

	s.insert(p1);
	s.insert(p2);
	s.insert(p3);
	s.insert(p4);

	for (set<Person, comparePerson>::iterator it = s.begin(); it != s.end(); it++)//此处<>内也要加上排序规则
	{
		cout << "姓名: " << it->m_Name << " 年龄: " << it->m_Age << endl;
	}
}

pair对组

  • 元素均由索引值和实值组成
pair<string,int> p ("Tom",10);
pair<string,int> p = make_pair("Tom",10);//pair的两种构造方式

p.first();p.second();//pair的访问方式

map/multimap

  • 所有元素均为pair对组类型,且在添加时候按照索引值排序,自定排序规则与set相同
  • map不允许容器中有重复索引值,multimap允许容器中有重复索引值
m.insert(pair<int,int> (1,10));
m.insert(make_pair(2,20));

m.erase(key);//删除索引值为key的元素
m.find(key);//查找索引值为key的元素,若找到了,则返回该元素对应迭代器(为指针),反之返回m.end()
s.count(key);//统计索引值为key的元素数量
(*it).first;//(*it)得到pair对组对象,用.访问内容
it->second;指针用箭头访问内容
#include <map>

class MyCompare {//自定排序规则类
public:
	bool operator()(int v1, int v2) {//重载小括号,形参注意区分前文自定义数据类型,无const与&
		return v1 > v2;	//默认从小到大排序,在此处实现从大到小排序
	}
};

void test01() 
{
	map<int, int, MyCompare> m;

	m.insert(make_pair(1, 10));
	m.insert(make_pair(2, 20));
	m.insert(make_pair(3, 30));
	m.insert(make_pair(4, 40));
	m.insert(make_pair(5, 50));

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

函数对象(仿函数)

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

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

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

  • 与直接调用普通函数相比,仿函数作为类可以有自己的成员属性

#include <string>

//1、函数对象在使用时,可以像普通函数那样调用, 可以有参数,可以有返回值
class MyAdd
{
public :
	int operator()(int v1,int v2)//仿函数需重载函数调用符号,即小括号
	{
		return v1 + v2;
	}
};

void test01()
{
	MyAdd myAdd;//类在使用时需要先实例化对象,即为函数对象
	cout << myAdd(10, 10) << endl;//调用方式与普通函数相同,输出20
}

//2、函数对象可以有自己的状态
class MyPrint
{
public:
	MyPrint()
	{
		count = 0;
	}
	void operator()(string test)//仿函数需重载小括号
	{
		cout << test << endl;
		count++; //统计使用次数
	}

	int count; //内部自己的状态
};
void test02()
{
	MyPrint myPrint;//类在使用时需要先实例化对象,即为函数对象
	myPrint("hello world");
	myPrint("hello world");
	myPrint("hello world");//调用方式与普通函数相同
	cout << "myPrint调用次数为: " << myPrint.count << endl;//输出3
}

//3、函数对象可以作为参数传递
void doPrint(MyPrint &mp , string test)
{
	mp(test);
}

void test03()
{
	MyPrint myPrint;
	doPrint(myPrint, "Hello C++");
}

int main() {

	//test01();
	//test02();
	test03();

	system("pause");

	return 0;
}
  • Pred谓词即为返回bool类型的仿函数

常用算法

  • 发现将仿函数传入算法形参时,需要带括号,与前文仿函数传入模板类区别!

遍历

#include <algorithm>
#include <vector>

//普通函数
void print01(int val) 
{
	cout << val << " ";
}
//函数对象
class print02 
{
 public:
	void operator()(int val) //重载小括号
	{
		cout << val << " ";
	}
};

//for_each算法基本用法
void test01() {

	vector<int> v;
	for (int i = 0; i < 10; i++) 
	{
		v.push_back(i);
	}

	//遍历算法
	for_each(v.begin(), v.end(), print01);//第三个形参传入普通函数名
	cout << endl;

	for_each(v.begin(), v.end(), print02());//第三个形参传入函数对象,且带括号
	cout << endl;
}

查找

#include <algorithm>

find(iterator beg, iterator end, value);//查找到返回位置迭代器,找不到则返回.end()
#include <algorithm>
#include <vector>
#include <string>

//内置数据类型
class GreaterFive
{
public:
	bool operator()(int val)
	{
		return val > 5;//查找条件为大于5
	}
};

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(), GreaterFive());//传入仿函数,带括号
	if (it == v.end()) {
		cout << "没有找到!" << endl;
	}
	else {
		cout << "找到大于5的数字:" << *it << endl;
	}
}

//自定义数据类型
class Person {
public:
	Person(string name, int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
public:
	string m_Name;
	int m_Age;
};

class Greater20
{
public:
	bool operator()(Person &p)//传入引用
	{
		return p.m_Age > 20;//查找条件为自定义数据类型的成员变量大于20
	}

};

void test02() {

	vector<Person> v;

	//创建数据
	Person p1("aaa", 10);
	Person p2("bbb", 20);
	Person p3("ccc", 30);
	Person p4("ddd", 40);

	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);

	vector<Person>::iterator it = find_if(v.begin(), v.end(), Greater20());//传入仿函数,带括号
	if (it == v.end())
	{
		cout << "没有找到!" << endl;
	}
	else
	{
		cout << "找到姓名:" << it->m_Name << " 年龄: " << it->m_Age << endl;
	}
}
adjacent_find(iterator beg, iterator end);//查找相邻重复元素,若查找到,则返回相邻重复元素的第一个位置的迭代器
///必须在有序数列中使用!!!速度快
bool binary_search(iterator beg, iterator end, value);//查找指定值,返回布尔类型

计算

count(iterator beg, iterator end, value);
count_if(iterator beg, iterator end, _Pred);//_Pred为谓词,此函数为条件统计函数

排序

sort(iterator beg, iterator end, _Pred);//默认由小到大
//系统内置greater<T>()可传入_Pred部分,用于将排序顺序变为由大到小
//若直接使用,为伪随机,如想真随机,则需要加入随机数种子:
#include <ctime>
srand((unsigned int)time(NULL));
//函数本体:
random_shuffle(iterator beg, iterator end);
#include <algorithm>
#include <vector>

class myPrint
{
public:
	void operator()(int val)
	{
		cout << val << " ";
	}
};

void test01()
{
	vector<int> v1;
	vector<int> v2;
	for (int i = 0; i < 10 ; i++) 
    {
		v1.push_back(i);
		v2.push_back(i + 1);
	}
        //创建新目标容器
	vector<int> vtarget;
	//目标容器需要提前开辟空间
	vtarget.resize(v1.size() + v2.size());
	//合并注意是要两个有序序列
	merge(v1.begin(), v1.end(), v2.begin(), v2.end(), vtarget.begin());//第五个参数即目标容器的起始迭代器
	for_each(vtarget.begin(), vtarget.end(), myPrint());
	cout << endl;
}
reverse(iterator beg, iterator end);

拷贝替换

copy(iterator beg, iterator end, iterator dest);//目标容器需提前开辟空间!
replace(iterator beg, iterator end, oldvalue, newvalue);
swap(container c1, container c2);//交换两容器元素,注意需同种容器
#include <algorithm>
#include <vector>

class myPrint
{
public:
	void operator()(int val)
	{
		cout << val << " ";
	}
};

class ReplaceGreater30//仿函数
{
public:
	bool operator()(int val)
	{
		return val >= 30;//替换条件为大于30
	}

};

void test01()
{
	vector<int> v;
	v.push_back(20);
	v.push_back(30);
	v.push_back(20);
	v.push_back(40);
	v.push_back(50);
	v.push_back(10);
	v.push_back(20);

	cout << "替换前:" << endl;
	for_each(v.begin(), v.end(), myPrint());
	cout << endl;

	//将容器中大于等于的30 替换成 3000
	cout << "替换后:" << endl;
	replace_if(v.begin(), v.end(), ReplaceGreater30(), 3000);//第三个形参为替换条件,带括号的仿函数,第四个形
  参为替换成的新值
	for_each(v.begin(), v.end(), myPrint());
	cout << endl;
}

算术生成算法

#include <numeric>//注意此处头文件与前面算法不同

accumulate(iterator beg, iterator end, value);//计算容器内元素累加值,value为起始值,一般为0
fill(iterator beg, iterator end, value);//用value值填充容器

集合算法

//要求两容器均为有序数列!

set_intersection(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);//取交集
//新容器开辟空间为vTarget.resize(min(v1.size(), v2.size()));

set_union(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);//取并集
//新容器开辟空间为vTarget.resize(v1.size() + v2.size());

set_difference(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);//取差集
//新容器开辟空间为vTarget.resize( max(v1.size() , v2.size()));
//注意取差集时,v1-v2与v2-v1不同!
#include <vector>
#include <algorithm>

class myPrint
{
public:
	void operator()(int val)
	{
		cout << val << " ";
	}
};

void test01()
{
	vector<int> v1;
	vector<int> v2;
	for (int i = 0; i < 10; i++)
    {
		v1.push_back(i);
		v2.push_back(i+5);
	}

	vector<int> vTarget;
	//取两个里面较小的值给目标容器开辟空间
	vTarget.resize(min(v1.size(), v2.size()));

	//返回目标容器的最后一个元素的迭代器地址
	vector<int>::iterator itEnd = 
        set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin());

	for_each(vTarget.begin(), itEnd, myPrint());//遍历时的末尾迭代器即为该算法返回的迭代器
	cout << endl;
}