c++基础学习补充

c++基础学习补充,第1张

文章目录
  • c++基础学习
    • 1.new,delete用法
    • 2.引用 &
    • 3.函数的默认参数
      • 3.1 如果某个位置有默认参数,则从该位置以后都必须有默认参数
      • 3.2 如果函数声明有默认参数,而函数实现不能有默认参数(声明和实现只能有一个有默认参数)
      • 3.3 占位参数
    • 4.函数重载
      • 4.1函数重载的要求
      • 4.2引用作为函数重载的条件
      • 4.3函数重载注意点
    • 6.类和对象
      • 6.1拷贝构造函数
      • 6.2构造函数调用方法
      • 6.3 深拷贝构造函数:
      • 6.4初始化列表
      • 6.5静态成员变量
      • 6.6静态成员函数
      • 6.7成员变量和成员函数的存储
      • 6.8 this 指针
      • 6.9 const修饰成员函数
    • 7. 友元
      • 7.1 全局函数做友元
      • 7.2 类做友元
      • 7.3 成员函数做友元
    • 8. 运算符重载
      • 8.1 ‘+’ 运算符重载
      • 8.2 '<<' 运算符重载
      • 8.3 '++' '--'递增运算符重载
      • 8.4 '=' 赋值运算符重载
      • 8.5 '==','!=','<','>' 重载关系运算符
      • 8.6 ' () ' 函数调用运算符的重载
    • 9.继承
      • 9.1 继承方式
      • 9.2 利用开发人员命令提示符查看对象模型
      • 9.3 继承时的构造和析构顺序
      • 9.4 如何通过子类访问父类数据
      • 9.5 继承同名静态成员处理方式
      • 9.6 多继承语法
      • 9.7 菱形继承问题
    • 10. 多态
    • virtual 的原理机制
      • 10.1 纯虚函数 抽象类
      • 10.2 虚析构和纯虚析构
    • 11. 文件
      • 11.1 文本文件读写
      • 11.2 二进制文件读写
    • 易错点、小知识
  • c++进阶学习
    • 1.函数模板
      • 1.1 函数模板基础
      • 1.2 函数模板和普通函数区别
      • 1.3 模板的局限性
    • 2. 类模板
      • 2.1 类模板和函数模板的区别
      • 2.2 **类模板的类在调用时才会创建,而普通类在一开始就会创建**
      • 2.3 类模板对象做函数参数
      • 2.4 类模板与继承
      • 2.5 类模板的类外实现
      • 2.6 类模板函数 分文件编写
      • 2.7 类模板与 友元
    • 3. STL 学习
      • 3.1 STL初识 vector容器
      • 3.2 Vector容器嵌套容器
      • 3.3 String 容器
        • 3.3.1 初始化 *** 作
        • 3.3.2 赋值 *** 作
        • 3.3.3 字符串拼接
        • 3.3.4 查找和替换
        • 3.3.5 字符串比较
        • 3.3.6 字符串 中的字符存取
        • 3.3.7 字符串的 插入 和 删除
        • 3.3.8 string 获取子串
      • 3.4 vector 容器
        • 3.4.1 vector 构造函数
        • 3.4.2 vector 容量和大小
        • 3.4.3 vector 插入和删除
        • 3.4.4 vector 数据存取
        • 3.4.5 vector 互换容器
        • 3.4.6 vector 预留空间
      • 3.5 deque 容器
        • 3.5.1 deque 构造函数
        • 3.5.2 deque 赋值 *** 作
        • 3.5.3 deque 大小 *** 作
        • 3.5.4 deque 插入和删除
        • 3.5.5 deque 数据存取
        • 3.5.6 sort() 后续学习
      • 3.6 stack 容器
        • 3.6.1 stack 常用接口
      • 3.7 queue 容器
        • 3.7.1 queue 常用接口
      • 3.8 list 容器
        • 3.8.1 list 构造函数
        • 3.8.2 list 赋值和交换
        • 3.8.3 list 大小 *** 作
        • 3.8.4 list 插入和删除
        • 3.8.5 list 数据存取
        • 3.8.6 list 反转和排序
      • 3.9 set / multiset 容器
        • 3.9.1 set / multiset 构造和赋值
        • 3.9.2 set 大小和交换
        • 3.9.3 set 插入和删除
        • 3.9.4 set 查找 和 统计
        • 3.9.5 set 和 multiset 区别
        • 3.9.6 pair 对组的创建
        • 3.9.7 set 排序
      • 3.10 map / multimap 容器
        • 3.10.1 map 构造和赋值
        • 3.10.2 map 大小和交换
        • 3.10.3 map 插入和删除
        • 3.10.3 map 查找和统计
        • 3.10.4 map 排序
      • 3.11 函数对象 = 仿函数
      • 3.12 谓词
        • 3.12.1 一元谓词
        • 3.12.2 二元谓词
      • 3.13 内建函数对象
        • 3.13.1 算数仿函数
        • 3.13.2 关系仿函数
        • 3.13.3 逻辑仿函数
      • 3.14 STL常用算法
        • 3.14.1 遍历算法
          • ==for_each()==
          • ==transform()==
        • 3.14.2 查找算法
          • **==find()==**
          • **==find_if()==**
          • **==adjacent_find()==**
          • **==binary_find()==**
          • **==count()==**
          • **==count_if()==**
        • 3.14.3 排序算法
          • ==sort()==
          • ==random_shuffle()== 洗牌算法
          • ==merge()==
          • ==reverse()==
        • 3.14.4 拷贝和替换算法
          • ==copy()==
          • ==replace()==
          • ==replace_if()==
          • ==swap()==
        • 3.14.5 算数生成算法
          • ==accumulate()==
          • ==fill()==
        • 3.14.6 集合算法
          • ==set_intersection()==
          • ==set_union()==
          • ==set_difference()==

c++基础学习 1.new,delete用法

new 是在堆上开辟空间,由程序员自己管理,delete删除空间;而栈空间是由OS自动管理和释放

2.引用 &

引用的本质是一个指针常量

int a = 10;
int& p = a;
//修改p也会修改a的值

int b = 20;
p = b;	//这不是指向别人,而是赋值 *** 作
//而引用是个不能再去指向别人的指针,即 int* const p 


/*引用作为返回值时函数可以作为左值*/
//例如:
int& func(){
    //int a = 10;			//不建议返回局部变量地址,因为局部变量存在栈中,函数结束时会被自动清除
    static int a = 10;   //静态变量会在全部程序结束时才会被释放内存
    return a; 
}

int main(){
    int& p = func();
    cout << p << endl;
    
    func() =  999;
    cout<< p << endl;
}

3.函数的默认参数 3.1 如果某个位置有默认参数,则从该位置以后都必须有默认参数
int func(int a = 10, int b , int c )			//error
int func(int a = 10, int b , int c = 1 )		//error
int func(int a , int b = 1, int c = 2)			//true
3.2 如果函数声明有默认参数,而函数实现不能有默认参数(声明和实现只能有一个有默认参数) 3.3 占位参数
void func(int a,int)	//后者为占位参数,展位参数也可以有默认值
4.函数重载 4.1函数重载的要求

参数类型不同、个数不同、顺序不同

void func(int);
void func(double);
--------------------------------------------------
void func();
void func(int);
--------------------------------------------------
void func(int,double);
void func(double,int);

函数返回值不可以作为函数重载的条件

int func();
void func();	//error
4.2引用作为函数重载的条件
#include
#include
using namespace std;

void func(int& a) {
	cout << "func int& 的使用" << endl;
}

void func(const int& a) {
	cout << "func const int& 的使用" << endl;
}

int main() {
	int a = 10;
	func(a);		//调用非const函数,因为 int& a = 10 不合法
	
	func(10);		//调用const函数,因为const int& a = 10,合法

	return 0;
}
4.3函数重载注意点

函数重载最好避免默认参数,否则会出现二义性

void func(int a,int b = 1);
void func(int a);

//此时下面函数就无法得知调用哪个
func(10);	//error
6.类和对象 6.1拷贝构造函数

拷贝构造函数格式

类名(const 类名& 形参名){
    
}
class Person{
public:  
   	Person(const Person& p){	//要const和引用方式
    	age = p.age;
	} 
    Person(int age){
        Person::age = age;
    }
private:
    int age;
}
6.2构造函数调用方法
#include
#include
using namespace std;

class Person {
public:
	Person() {
		cout << "默认构造函数的 age is " << age << endl;
	}

	Person(int age) {
		Person::age = age;
		cout << "含int参数的构造函数的 age is " << age << endl;
	}

	Person(const Person& p) {
		age = p.age;
		cout << "拷贝构造函数的 age is " << age << endl;
	}
private:
	int age;
};

int main() {
	/*括号法*/
	Person p1;		//默认构造函数
	Person p2(10);	//带参数构造函数
	Person p3(p2);	//拷贝构造函数

	cout << "----------------------------------------------" << endl;
	/*显示法*/
	Person p4;
	Person p5 = Person(99);	//后者Person(99)为匿名对象,特点:当前行执行结束后,系统会立即回收掉匿名对象
	Person p6 = Person(p5);
	cout << "----------------------------------------------" << endl;

	//Person(p2);    //error
	//注意:不要用拷贝构造函数去初始化匿名对象,编译器会认为Person(p3) 等价于 Person p3  -->  重定义


	/*隐式法 */
	Person p7 = 10;
	Person p8 = p7;
	cout << "----------------------------------------------" << endl;
	return 0;
}

6.3 深拷贝构造函数:

解决浅拷贝时堆区的内存在普通析构函数释放内存时会造成重复释放

/*以下程序error*/

#include
#include
using namespace std;

class Person {
public:
	Person(int age, int height) {
		cout << "构造函数" << endl;
		Person::age = age;
		Person::height = new int(height);
	}
	~Person() {
		cout << "析构函数" << endl;
		delete height;				
		height = NULL;
	}
private:
	int age;
	int* height;
};

int main() {
	Person p1(10, 20);
	Person p2(p1);


	return 0;
}

这就导致析构函数重复释放内存

浅拷贝只是

Person(const Person& p){
    age = p.age;
    height = p.height;		//这里并未重新分配内存
}

所以为了解决这个问题,要自己写拷贝构造函数(深拷贝)

Person(const Person& p) {
		cout << "拷贝构造函数" << endl;
		age = p.age;
		height = new int(*p.height);	//重新分配空间
}
6.4初始化列表
class Person {
public:
	Person() :a(10), b(1.2), c(2) {				
	}
	Person(int x, double y, int z) :a(x), b(y), c(z) {

	}
    
private:
	int a;
	double b;
	int c;
};

6.5静态成员变量
  1. 所有成员共享一份数据;

  2. 编译阶段就分配内存

​ 3.类内声明,类外初始化 *** 作

class A {
public:
	static int a;
};

int A::a = 1;	//类外初始化

int main() {
	A aa;
	cout << aa.a << endl;	//output 1

	A bb;
	bb.a = 22;
	cout << aa.a << endl;	//output 22

	return 0;
}

访问方式

  1. 可以通过对象访问
  2. 可以通过类名访问
6.6静态成员函数
  1. 所有对象共享同一个函数
  2. 静态成员函数只能访问静态成员变量
6.7成员变量和成员函数的存储

成员变量和成员函数是分开存储的

class Person{
    int a;				//非静态成员变量,属于类的对象上
    
    static int b;		//静态成员变量,不属于类对象上
    
    void func();		//非静态成员函数,不属于类对象上
    
    static void func2();//静态成员函数,不属于类的对象上
}

空类 sizeof 是1

class Person{
    
}
int main(){
    Person p;
    cout<< sizeof(p)<
6.8 this 指针

this指针指向被调用的成员函数所属对象:谁调用这个成员函数,this就指向谁

this指针的本质是 指针常量 指针的指向是不可以修改的

//this 相当于
Person* const this;

//而 const Person* const this 表示this指向的值也不能修改
class Person{
public:
	Person(int age){
        this->age = age;
    }
private:
    int age;
}
/* Example 2 */

#include
using namespace std;

class Person {
public:
	Person(int age) {
		this->age = age;
	}
	Person& AddAge(Person p) {
		this->age += p.age;
		return *this;
	}
	int getAge() {
		return age;
	}
private:
	int age;
};


int main() {
	Person p1(10);
	p1.AddAge(p1).AddAge(p1);		//链式编程
	cout << p1.getAge() << endl;	//output 40
	return 0;
}
6.9 const修饰成员函数
  1. 常函数

    1. 成员函数后加const后我们称为这个函数为常函数
    2. 常函数内不可以修改成员属性
    3. 成员属性声明时加关键字mutable后,在常函数中依然可以修改

    在成员函数后加const,修饰的是this所指对象,使该对象的值不能被修改

    class Person {
    public:
    	void Info() const {
    		age = 10;		//error
    		weight = 20.1;	//true
    	}
    private:
    	int age;
    	mutable double weight;
    };
    
  2. 常对象

    1. 声明对象前加const称该对象为常对象
    2. 常对象只能调用常函数
class Person {
public:
	int test1;
	mutable int test2;
};


int main() {
	
	const Person p1 ;
	p1.test1 = 100;		//error

	p1.test2 = 100;		//true

	return 0;
}
//常对象不能调用普通成员函数,因为普通成员函数有可能会改变成员变量

class Person {
public:
	void Info() const {			//常函数
		age = 10;		//error
		weight = 20.1;	//true
	}
	void Info2() {				//普通成员函数

	}

	int test1;
	mutable int test2;

private:
	int age;
	mutable double weight;
};


int main() {
	
	const Person p1 ;	//常对象
    
	p1.test1 = 100;		//error

	p1.test2 = 100;		//true

	p1.Info();			//true

	p1.Info2();			//error

	return 0;
}
7. 友元 7.1 全局函数做友元
#include
#include
using namespace std;

class Building {

	friend void goodGay(Building* building);	//只需要写在类里面就行

public:
	Building() {
		SittingRoom = "客厅";
		BedRoom = "卧室";
	}

	string SittingRoom;
private:
	string BedRoom;
};


void goodGay(Building *building) {
	cout << "good gay is visiting your building :" <SittingRoom<< endl;	 
	cout << "good gay is visiting your building :" << building->BedRoom << endl;	//可以访问私有成员变量了
}

void test01() {
	Building building;
	goodGay(&building);
}

int main() {
	test01();

	return 0;
}

7.2 类做友元

只要在类内声明 class friend 类名 就可以访问私有成员变量

#include
#include
using namespace std;

class Man2;
class Man1;

class Man1
{
	friend class Man2;
public:
	Man1() {
		box1 = "private box";
		box2 = "public box";
	}

	string box2;
private:
	string box1;
};

class Man2
{
public:
	void visit(Man1* man1);
	void visit();
};

void Man2::visit(Man1* man1) {
	cout << man1->box2 << "\t" << man1->box1 << endl;
}
void Man2::visit() {
	Man1* man = new Man1;
	cout << man->box1 << "\t" << man->box2 << endl;
}

void test() {
	Man1 man;
	Man2 man2;
	man2.visit();
	man2.visit(&man);
}


int main() {
	test();
	return 0;
}
7.3 成员函数做友元

要注意成员函数做友元时要在访问的那个类定义以后再实现,毕竟这个友元函数是要访问那个类的数据的

#include
#include
using namespace std;

class Person;

class Friend {
public:
	Friend();

	void visit();
	void visit(Person* p);

	Person* person;
};

class Person {
	friend void Friend::visit();
	friend void Friend::visit(Person* p);
public:
	Person() {
		box1 = "public box";
		box2 = "private box";
	}
	string box1;
private:
	string box2;
};

Friend::Friend() {
	person = new Person;
}

void Friend::visit() {					//在Person类定义后再实现,不然会报错
	cout << "This is No.1" << endl;
	cout << person->box1 << endl;
	cout << person->box2 << endl;
}
void Friend::visit(Person* p) {			//同理
	cout << "This is No.2" << endl;
	cout << p->box1 << endl;
	cout << p->box2 << endl;
}


int main() {
	Person p;
	Friend f;
	f.visit();
	f.visit(&p);
	return 0;
}
8. 运算符重载

运算符重载格式

返回值类型 operator 运算符名称(形参列表){
    //TODO
}
8.1 ‘+’ 运算符重载
#include
#include
using namespace std;

//通过成员函数重载
class Person {
public:
	Person operator+(Person p) {	//此处可以改为 Person operator+(Person& p)	使用引用 而避免使用拷贝构造函数加快速度
		Person temp;
		temp.a = this->a + p.a;
		temp.b = this->b + p.b;
		return temp;
	}
	Person operator+(int num) {		//运算符+重载后函数重载
		Person temp;
		temp.a = this->a + num;
		temp.b = this->b + num;
		return temp;
	}
	int a;
	int b;
};

//通过全局函数运算符重载
Person operator+(Person p1, Person p2) {	//此处可以改为 Person operator+(Person& p1,Person& p2)	使用引用 而避免使用拷贝构造函数加快速度
	Person temp;
	temp.a = p1.a + p2.a;
	temp.b = p1.b + p2.b;
	return temp;
}
//全局函数运算符重载后函数重载
Person operator+(Person& p1, int num) {	
	Person temp;
	temp.a = p1.a + num;
	temp.b = p1.b + num;
	return temp;
}

int main() {
	Person p1;
	p1.a = 10;
	p1.b = 20;

	Person p2;
	p2.a = 99;
	p2.b = 1;


	Person p3 = p1 + p2;
	cout << p3.a << "\t" << p3.b << endl;


	Person p4 = p1 + 9;
	cout << p4.a << "\t" << p4.b << endl;
	return 0;
}
//注意这里成员函数调用的本质:
Person p3 = p1.operator+(p2);

//全局函数的本质:
Person p3 = operator+(p1,p2);

运算符重载也可以实现函数重载

8.2 ‘<<’ 运算符重载

一般不会利用成员函数重载<<

#include
#include
using namespace std;

class Person {
public:
	string name;
	int age;
};

//void operator<<(ostream &cout, Person& p) {		//如果知识返回void 那么就无法链式追加输出:如 cout<< p<< endl;就会报错
//	cout << p.name << "\t" << p.age;
//}
//ostream& operator<<(ostream& cout, Person& p) {		//改为这样即可链式输出 即 cout<< p << p << endl;
//	cout << p.name << "\t" << p.age;
//	return cout;
//}
ostream& operator<<(ostream& out, Person& p) {		//改为 out 也一样
	out << p.name << "\t" << p.age;
	return cout;
}

int main() {
	Person p;
	p.name = "Hello";
	p.age = 99;
	cout << p << endl;
	return 0;
}
8.3 ‘++’ '–'递增运算符重载

前后置++区分用占位符来区分

​ 值/引用 operator ++() ---->前置

​ 值/引用 operator ++(int)---->后置

  1. 前置++

    前置++最好 引用返回

  2. 后置++

​ 后置++,用值返回

#include
#include
using namespace std;

class Person {
	friend ostream& operator<<(ostream& out, Person p);
public:
	Person(int age) {
		Person::age = age;
	}
	Person& operator++() {		//前置++
		this->age++;
		return *this;
	}
	Person operator++(int) {	//后置++
		Person temp = *this;
		this->age++;
		return temp;
	}
private:
	int age;
};

ostream& operator<<(ostream& out, Person p) {
	out << p.age;
	return out;
}

int main() {

	Person p(9);
	cout << p++ << "\t" <

递减运算符重载

	Person& operator--() {			//前置递减		
		age--;
		return *this;
	}
	Person operator--(int) {		//后置递减	
		Person temp = *this;
		this->age--;
		return temp;
	}
8.4 ‘=’ 赋值运算符重载
#include
#include
using namespace std;

class Person {
public:
	Person() {

	}
	Person(int age) {
		Person::age = new int(age);
	}
	~Person() {
		if (age != NULL) {				
			delete age;
			age = NULL;
		}
	}

	Person& operator=(Person& p) {		//注意这里返回值为引用,这样可以链式计算(a = b = c)
		if (age != NULL) {
			delete age;
			age = NULL;
		}
		age = new int(*p.age);			//如果重载运算符函数不重新分配内存,则在析构函数时会重复释放内存
		return *this;
	}

	int* age;


};

void test() {
	Person p1(1);
	Person p2;
	Person p3;
	p3 = p2 = p1;
	cout << "p1 age is " << *p1.age << endl;
	cout << "p2 age is " << *p2.age << endl;
	cout << "p3 age is " << *p3.age << endl;
}

int main() {
	test();
	return 0;
}
8.5 ‘==’,‘!=’,‘<’,‘>’ 重载关系运算符

思路与上面一样

#include
#include
using namespace std;

class Person {
public:
	Person(string name) {
		this->name = name;
	}
	bool operator==(Person& p) {
		if (this->name == p.name)
			return true;
		else
		{
			return false;
		}
	}

	string name;
};


int main() {
	Person p1("Tom");
	Person p2("Tom");
	if (p1 == p2) {
		cout << "Same" << endl;
	}
	else
	{
		cout << "Different" << endl;
	}


	return 0;
}
8.6 ’ () ’ 函数调用运算符的重载

这与后面 [仿函数](# 3.11 函数对象 = 仿函数) 知识有些关系

class Person {
public:
	Person() {

	}
	Person(string name) {
		this->name = name;
	}
	string name;

	void operator()(string data) {
		cout << data << endl;
	}
	void operator()(int a, int b) {
		cout << a + b << endl;
	}
};

int main() {
	Person p("Hello");
	p(p.name);

	Person()(100, 200);		//这里使用了匿名对象 + ‘()’ 重载
	return 0;
}

仿函数非常灵活,没有固定写法,以后会再进行学习

9.继承
//语法
class 子类: 继承方式	父类
9.1 继承方式

  1. public 权限:类内、类外均可以访问

  2. protected 权限:类内可以访问,类外不可以

  3. private 权限:类内类外均无法访问

9.2 利用开发人员命令提示符查看对象模型
利用开发人员命令提示工具查看对象模型
    1.	跳转到文件所在位置
    2.	cd 到具体文件路径
    3.	cl /d1 reportSingleClassLayout类名 文件名

E:\WorkSpace\CPP\面向对象>E:

E:\WorkSpace\CPP\面向对象>cd E:\WorkSpace\CPP\面向对象

E:\WorkSpace\CPP\面向对象>dir
 Volume in drive E is 新加卷
 Volume Serial Number is 122E-F072

 Directory of E:\WorkSpace\CPP\面向对象

2022/03/10  11:12    <DIR>          .
2022/03/10  11:12    <DIR>          ..
2022/03/07  15:07    <DIR>          Debug
2022/03/07  15:06    <DIR>          x64
2022/03/10  11:12               229 面向对象.cpp
2022/03/07  14:57             1,444 面向对象.sln
2022/03/07  15:06             7,206 面向对象.vcxproj
2022/03/07  15:06               974 面向对象.vcxproj.filters
2022/03/07  14:57               168 面向对象.vcxproj.user
               5 File(s)         10,021 bytes
               4 Dir(s)  1,026,584,707,072 bytes free

E:\WorkSpace\CPP\面向对象>cl /d1 reportSingleClassLayoutDog 面向对象.cpp
用于 x86 的 Microsoft (R) C/C++ 优化编译器 19.31.31104 版
版权所有(C) Microsoft Corporation。


保留所有权利。


面向对象.cpp class Dog size(16): +--- 0 | +--- (base class Animal) 0 | | a 4 | | b 8 | | c | +--- 12 | d +--- Microsoft (R) Incremental Linker Version 14.31.31104.0 Copyright (C) Microsoft Corporation. All rights reserved. /out:面向对象.exe 面向对象.obj

9.3 继承时的构造和析构顺序

​ 构造顺序:先父后子

​ 析构顺序:先子后父

9.4 如何通过子类访问父类数据

​ 访问子类自己的数据:直接访问即可

​ 访问父类同名成员:需要加上作用域

class Animal {
public:
	int a;
};

class Dog :public Animal {
public:
	int a;
};


int main() {
	Dog p;
	p.a = 99;			//这样就是对自己的同名数据进行设置		
	p.Animal::a = 1;	//加上作用域即可访问父类
	cout << "子类a " << p.a << endl;
	cout << "父类a " << p.Animal::a << endl;
	return 0;
}

访问同名函数也一样

#include
#include
using namespace std;

class Animal {
public:
	int a;
	void func() {
		cout << "Base func()" << endl;
	}
	void func(int) {
		cout << "Base func(int)" << endl;
	}
};

class Dog :public Animal {
public:
	int a;
	void func() {
		cout << "Son func()" << endl;
	}
};


int main() {
	Dog p;
	p.func();
	p.Animal::func();
	
	// 若父类有函数重载且子类没有相同函数,子类直接访问是访问不到的,因为子类中出现和父类同名的函数时子类会把父类函数全部屏蔽,包括重载函数
	p.func(100);		//error
	p.Animal::func(100);
	return 0;
}
9.5 继承同名静态成员处理方式
#include
using namespace std;

class Base {
public:
	static int a;
};
int Base::a = 1;

class Son :public Base{
public:
	static int a;
};
int Son::a = 2;

void test() {
	Son son;
	//通过对象访问
	cout <<"son'a" << son.a << endl;
	cout << "Base'a" << son.Base::a << endl;

	//通过类名访问
	cout << "son'a" << Son::a << endl;
	cout << "Base'a" << Base::a << endl;

	//第一个::代表通过类名访问,第二个::代表访问父类作用域下
	cout << "Base'a" << Son::Base::a << endl;

}

int main() {
	test();
	return 0;
}
9.6 多继承语法
//不建议多继承
class A:public B,public C,...{
    
}

使用同名变量时需要加作用域 类 名::变量

9.7 菱形继承问题

class Animal{
public:
    int age;
}
此时 sheep 类 和 camel 类都继承了age变量,所以羊驼类继承了两个age

利用虚继承可以解决

[利用开发人员命令提示符](# 10.2 利用开发人员命令提示符查看对象模型)

class B的vbptr地址 0,加上偏移量8就是变量a;
class C的vbptr地址 4,加上偏移量4就是变量a;
所以通过虚继承最后继承的数据只有一份
10. 多态 virtual 的原理机制
  1. virtual 成员变量:

  1. virtual 成员函数

    1. 多态分为两类:
      1. 静态多态:函数重载 和 运算符重载–> 静态多态,复用函数名
      2. 动态多态:派生类和虚函数实现运行时多态
    2. 静态多态和动态多态区别
      1. 静态多态的函数地址早绑定 编译阶段确定函数地址
      2. 动态多态的函数地址晚绑定 运行阶段确定函数地址
class Animal {
public:
	void Speak() {
		cout << "This is Animal" << endl;
	}
 };

class Dog :public Animal{
public:
	void Speak() {
		cout << "This is Dog" << endl;
	}
};

class Cat:public Animal {
public:
	void Speak() {
		cout << "This is Cat" << endl;
	}
};

void DoSpeak(Animal& animal) {	//父类引用 指向 子类对象
	animal.Speak();
}

int main() {
	Cat cat;
	DoSpeak(cat);		//output This is Animal
}
//因为在编译阶段已经绑定了函数地址
//如果想执行让猫说话,那么这个函数地址就不能提前绑定

在 Animal 类中 改 void Speak(){}
为 virtual void Speak(){}

动态多态条件

  1. 有继承关系

  2. 子类重写父类虚函数

    子类中重写父类虚函数时可以加virtual关键字,也可以不加

动态多态的使用

  1. 父类 指针 或 引用 执行子类对象

多态原理

视频

图解:

10.1 纯虚函数 抽象类
//纯虚函数的语法格式
virtual 返回值类型  函数名  (参数列表) = 0 ;

当类中有 **纯虚函数 **,这个类就叫抽象类

抽象类特点:

  1. 无法实例化对象
  2. 子类必须重写抽象类中的纯虚函数,否则也属于抽象类,即也无法实例化对象
#include
using namespace std;

class interfaces {
public:
	virtual int getInfo() = 0;
};

class A :public interfaces {
public:
	int getInfo() {
		return age;
	}
	void setInfo() {
		age = 10;
	}
private:
	int age;
};

int main() {
	
	A a;
	a.setInfo();
	cout << a.getInfo() << endl;
	
	interfaces* i = new A;		//多态实现
	cout << i->getInfo() << endl;
	return 0;
}

i 无法使用setInfo() 因为父类并没有这个函数

10.2 虚析构和纯虚析构

多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的西沟代码

实例:

#include
#include
using namespace std;

class Base {
public:
	Base() {
		cout << "this is Base" << endl;
	}

	virtual void show() = 0;

	~Base() {
		cout << "this is Base out" << endl;
	}
};

class Son :public Base {
public:
	Son(string name) {
		cout << "this is Son" << endl;
		this->name = new string(name);
	}

	void show() {
		cout << *name << " say Hi~ " << endl;
	}

	~Son() {
		cout << "this is Son out" << endl;
		if (NULL != name) {		
			delete name;
			name = NULL;
		}	
	}
	string* name;
};

void test() {
	Base* a = new Son("Tom");	//利用多态
	a->show();
	delete a;
}


int main() {
	test();
	return 0;
}

此时 并没有调用 Son 的析构函数 ,导致内存释放不干净

//将父类的析构函数改为虚函数,则可以使子类调用自己的析构函数,从而释放内存
virtual ~Base() {
		cout << "this is Base out" << endl;
}

//父类的析构函数写成纯虚析构函数
virtual ~Base() = 0			//error 父类的析构函数必须要有代码实现,因为在多态使用中 Base* p = new Son("Tom");
    						// 由于子类对象析构函数会隐式调用父类的虚构函数,而父类析构函数没有实现则会报错
    

class Base {
public:
	Base() {
		cout << "this is Base" << endl;
	}

	virtual void show() = 0;

	virtual ~Base() = 0;	//纯虚析构函数
};

Base::~Base() {				//尽管是纯函数,但是也需要去实现
	cout << "this is base out" << endl;
}

以上代码可能会让人觉得多此一举,因为反正你都实现了析构函数,为什么要将析构函数声明成纯虚函数呢?

事实上,有时我们想让一个类称为抽象类,而又没有其他可以声明成纯虚函数的函数,此时我们就可以将析构函数设置为纯虚函数达到实现抽象类的目的,抽象类可以不用实例化,如:

//这里我不想Base可以实例化,但是我又需要在子类中利用父类的某些功能(这里是show()),而又因为多态的关系 父类没有的函数在多态中无法调用,所以show是虚函数,只好将父类的析构函数变成纯虚函数以达到父类变成抽象类

class Base {
public:
	virtual void show() {
		cout << "this is Base" << endl;
	}
	virtual ~Base() = 0;
};
Base::~Base() {

}

class Son :public Base {
public:
	virtual void show() {
		Base::show();
		cout << "this is Son" << endl;
	}
};

int main() {
	Base* p = new Son;
	p->show();

	return 0;
}
11. 文件
//头文件  
  1. 文本文件:以ASCII码形式存储
  2. 二进制文件:以二进制形式存储
// *** 作文件 三大类
ofstreanm:	写 *** 作		//output file
ifstream:	读 *** 作		//input file
fstream:	读写 *** 作
11.1 文本文件读写
//写 *** 作顺序

ofstream ofs;

ofs.open("文件路径",打开方式);

ios::in			//为 读 而打开文件
ios::out		//为 写 而打开文件
ios::ate		//初始位置:文件尾
ios::app		//追加方式写文件
ios::trunc		//如果文件存在先删除,再创建
ios::binary		//二进制方式
文件打开方式可以配合使用:ios:binary | ios:out   
    
//写数据
ofs << "写入的数据";

//关闭文件
ofs.close();

例:

#include
#include
using namespace std;

int main() {
	ofstream ofs;
	ofs.open("1.txt", ios::out);
	ofs << "你好" << endl;
	ofs << "杨皓翔" << endl;
	ofs.close();
	return 0;
}
//读 *** 作顺序

ifstream ifs;

ifs.open("文件路径",打开方式);

ios::in		//为 读 而打开文件
ios::out		//为 写 而打开文件
ios::ate		//初始位置:文件尾
ios::app		//追加方式写文件
ios::trunc	//如果文件存在先删除,再创建
ios::binary	//二进制方式
文件打开方式可以配合使用:ios:binary | ios:out   
    
//写数据
ifs << "写入的数据";

//关闭文件
ifs.close();

例:

#include
#include
using namespace std;

int main() {
	ifstream ifs;
	ifs.open("1.txt", ios::in);
	
	if (!ifs.is_open()) {			//判断打开是否成功函数
		cout << "文件打开失败" << endl;
		return 0;
	}

	//第一种
	/*char buf[1024] = { 0 };
	while (ifs >> buf) {
		cout << buf << endl;
	}*/

	//第二种
	/*char buf[1024] = { 0 };
	while (ifs.getline(buf,sizeof(buf))){
		cout << buf << endl;
	}*/

	//第三种
	/*string buf;
	while (getline(ifs,buf))
	{
		cout << buf << endl;
	}*/

	//第四种,不推荐,效率低
	char c;
	while (   (c = ifs.get()) != EOF   )
	{
		cout << c ;				//这里加endl 就不会输出结果
	}

	ifs.close();
	return 0;
}
11.2 二进制文件读写

二进制方式写文件主要利用流对象调用成员函数write

函数原型:ostream& write(const cha* buffer,int len)

#include
#include
using namespace std;

class Person {
public:
	string name;
	int age;
};


int main() {
	Person p;
	p.name = "里斯";
	p.age = 1;

	ofstream ofs;
	ofs.open("Person.txt", ios::out | ios::binary);	//二进制方式写到文件
	ofs.write((const char*)&p, sizeof(Person));		//这里因为write函数的参数要求,强转p为(const char*)&才能使用write函数
	ofs.close();
	return 0;
}
#include
#include
using namespace std;

class Person {
public:
	string name;
	int age;
};


int main() {
	//Person p;
	//p.name = "里斯";
	//p.age = 1;

	//ofstream ofs;
	//ofs.open("Person.txt", ios::out | ios::binary);	//二进制方式写到文件
	//ofs.write((const char*)&p, sizeof(Person));		//这里因为write函数的参数要求,强转p为(const char*)才能使用write函数
	//ofs.close();


	Person p2;
	ifstream ifs;
	ifs.open("Person.txt", ios::in | ios::binary);

	if (!ifs.is_open()) {
		cout << "open false" << endl;
	}

	ifs.read((char*)&p2, sizeof(Person));			  //这里因为read函数的参数要求,强转p为(char*)才能使用read函数

	cout << p2.name << "\t" << p2.age << endl;

	ifs.close();

	return 0;
}

易错点、小知识
  1. 如果在函数形参里写了数组 void output(int arr[]) 此时在计算数组长度是使用 int len = sizeof(arr) / sizeof(int) 计算是错误的,因为在函数中形参 int arr[] 被当作 int* arr 指针来看待 ,此时sizeof(arr)计算的是一个指针大小
int main(){
    int arr [] = {1,2,3,4};
    int len = sizeof(arr) / sizeof(int);	//true,  len = 4
}
void output(int arr[]){
    int len = sizeof(arr) / sizeof(int);	//false ,计算的不是数组长度
}
  1. 随机数

    rand() % 41 + 60 //60 ~ 100 的随机数

    rand() % 41 可以取到 0 ~ 40 的随机数 + 60 则可以取到 60 ~ 100


c++进阶学习
  • c++另一种编程思想称为 泛型编程,主要利用技术为 模板
  • c++提供两种模板机制:函数模板和类模板
1.函数模板 1.1 函数模板基础

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

//语法
template
函数声明或定义

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

实例:

#include
using namespace std;


template
void Swap(T& a,T& b) {
	T temp = a;
	a = b;
	b = temp;
}


int main() {
	int a = 1;
	int b = 2;
	Swap(a, b);
	cout << a << "\t" << b << endl;		//output 2	1


	double x = 10.3;
	double y = 12.2;
	Swap(x, y);
	cout << x << "\t" << y << endl;		//output 12.2	10.3

	return 0;
}
//1.自动类型推导	Swap(a,b);	
//必须推导出一致的数据类型T才可以使用  --- 上面函数如果 a,b类型不同则无法使用
//模板必须要确定出T的数据类型,才可以使用  --- 
template
void func(){
    cout<<"func 调用"<();	//right,随便指定一个类型就能用
}



//2.显示指定类型
Swap(a,b);
1.2 函数模板和普通函数区别
  1. 调用顺序

    #include
    using namespace std;
    
    
    int add(int a, int b) {
    	cout << "normal" << endl;
    	return a + b;
    }
    
    template
    T add(T a, T b) {
    	cout << "template" << endl;
    	return a + b;
    }
    
    int main() {
    	int a = 10;
    	int b = 9;
    	int sum = add(a, b);		//优先调用普通函数
    	cout << sum << endl;		
    
    	return 0;
    }
    
    1. 普通函数和函数模板均可以实现,则优先调用普通函数

    2. 可以通过空模板参数列表强制调用函数模板

      #include
      using namespace std;
      
      
      int add(int a, int b) {
      	cout << "normal" << endl;
      	return a + b;
      }
      
      template
      T add(T a, T b) {
      	cout << "template" << endl;
      	return a + b;
      }
      
      int main() {
      	int a = 1;
      	int b = 2;
      	//通过空模板参数列表,强制调用函数模板
      	add<>(a, b);		//output template
      
      	return 0;
      }
      
    3. 函数模板重载

      #include
      using namespace std;
      
      template
      T add(T a, T b) {
      	return a + b;
      }
      
      //函数模板重载
      template
      T add(T a, T b, T c) {
      	return a + b + c;
      }
      
      int main() {
      	int a = 1;
      	int b = 2;
      	int c = 3;
      	cout << add<>(a, b) << endl;
      	cout << add<>(a, b,c) << endl;
      	
      	return 0;
      }
      
    4. 如果有更好的匹配则优先调用函数模板

      #include
      using namespace std;
      
      template
      T add(T a, T b) {
      	cout << "template" << endl;
      	return a + b;
      }
      
      int add(int a,int b) {
      	cout << "normal" << endl;
      	return a + b ;
      }
      
      int main() {
      	char a = 'a';
      	char b = 'b';
      	cout << add(a, b) << endl;	//output template,虽然可以调用普通函数,但是函数模板在这里更加匹配
      	
      	return 0;
      }
      
  2. 普通函数调用时可以发生自动类型转换(隐式类型转换);

    函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换;

    如果利用显示指定类型的方式,可以发生隐式类型转换;

    #include
    using namespace std;
    
    
    int add(int a, int b) {
    	return a + b;
    }
    
    template
    T template_add(T a, T b) {
    	return a + b;
    }
    
    int main() {
    	int a = 10;
    	int b = 9;
    	char c = 'c';
    
    	cout << add(a, b) << endl;				//output 19
    	cout << add(a, c) << endl;				//output 109, c ASCII = 99,自动发生隐式转换
    
    	cout << template_add(a, b) << endl;		//output 19
    	cout << template_add(a, c) << endl;		//error
    
    	cout << template_add(a, c) << endl;//output 109,	显示指定类型
    
    
    	return 0;
    }
    
1.3 模板的局限性
template
bool Compare(T a,T b){
    if(a == b)
        return true;
    else
        return false;
}
class Person{
public:
    Person(string name,int age){
        this->name = name;
        this->age = age;
    }
	string name;
    int age;
}
int main(){
    Person p1("Tom",1);
    Person p2("Jerry",2);
    bool ret = Compare(p1,p2);		//error 无法比较
    cout<

解决办法

  1. 运算符重载

  2. 利用具体化Person的版本实现代码,具体化优先调用

    template<> bool Compare(Person p1,Person p2){
        if(p1.name == p2.name && p1.age == p2.age){
            return true;
        }
        else{
            return false;
        }
    }
    

    完整代码

    #include
    #include
    using namespace std;
    
    template
    bool Compare(T a,T b) {
    	if (a == b)
    		return true;
    	else
    		return false;
    }
    
    class Person {
    public:
    	Person(string name, int age) {
    		this->name = name;
    		this-> age = age;
    	}
    	string name;
    	int age;
    };
    
    template<> bool Compare(Person p1, Person p2) {
    	if (p1.name == p2.name && p1.age == p2.age) {
    		return true;
    	}
    	else {
    		return false;
    	}
    }
    
    int main() {
    	Person p1("Tom", 1);
    	Person p2("Jerry", 3);
    	bool ret = Compare(p1, p2);
    	if (ret) {
    		cout << "==" << endl;
    	}
    	else
    	{
    		cout << "!=" << endl;
    	}
    	return 0;
    }
    

学习模板可能并非是为了写模板,而是为了在STL能够运用系统提供的模板

2. 类模板
//语法 
template

实例:

#include
#include
using namespace std;

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



int main() {
	Person p("Tom", 1);

	return 0;
}
2.1 类模板和函数模板的区别
  1. 类模板没有自动函数类型推导的使用方式

    Person p("Tom",1);				//false
    Person p("Tom",1);  //true
    
  2. 类模板在模板参数列表中可以有默认参数,模板函数就不行

    template	//默认参数 int
    class Person{
    public:
        Person(NameType name,AgeType age){
            this->name = name;
            this->age = age;
        }
    	NameType name;
        AgeType age;
    }
    
    int main(){
        Person p("Tom",1);		//false
        Person p("Tom",1);			//true
        return 0;
    }
    
2.2 类模板的类在调用时才会创建,而普通类在一开始就会创建
#include
#include
using namespace std;

class A {
public:
	void func1() {
		cout << "this is A's func " << endl;
	}
};
class B {
public:
	void func2() {
		cout << "this is B's func" << endl;
	}
};

template
class Person {
public:
	T obj;
	void function() {
		obj.func1();
		obj.func2();
	}
};



int main() {
	return 0;
}

上面这段代码编译通过,因为类模板在调用时才会创建,所以在还没有调用时 obj.func1() obj.func2()不论T 类型的 obj 是否有这个函数,编译都会通过

int main(){
    Person p;
    p.function();		//error
}

这时,类模板被调用了,此时,由于obj 没有 func2() 函数,故报错

2.3 类模板对象做函数参数

三种传入方式

  1. 指定传入的类型 — 直接显示对象的数据类型 (最常用)

    #include
    #include
    using namespace std;
    
    template
    class Person {
    public:
    
    	Person(T1 name, T2 age) {
    		this->name = name;
    		this->age = age;
    	}
    
    	T1 name;
    	T2 age;
    	void show() {
    		cout << name << "\t" << age << endl;
    	}
    };
    
    //指定传入模板类为 Person
    void Print(Person& p) {
    	p.show();
    }
    
    int main() {
    
    	Person p("Tom", 1);
    	Print(p);
    
    	return 0;
    }
    
  2. 参数模板化 — 将对象中的参数变为模板进行传递

    template 
    void Print2(Person& p) {
    	p.show();
    }
    

    typeid(T1).name()可以查看T1的类型

  3. 整个类模板化 — 将这个对象类型 模板化进行传递

    template
    void Print3(T& p) {
    	p.show();
    }
    
2.4 类模板与继承
  • 当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型,如果不指定,编译器无法给子类分配内存
  • 如果想灵活指定父类中T的类型,子类也需要变为模板类

改为

class Son:public Faather{
public:

}

template
class Daugther :public Father {
public:
	Daugther(T name) {
		this->name = name;
	}
	void show() {
		cout << this->name << endl;
	}
};
2.5 类模板的类外实现
#include
#include
using namespace std;

template
class Person {
public:
	Person(T name);
	void show();
	T name;
};

//构造函数
template
Person::Person(T name) {
	this->name = name;
}

//类模板 成员函数
template
void Person::show() {
	cout << name << endl;
}


int main() {
	Person p("Tom");
	p.show();

	return 0;
}

为了表示是类模板的构造函数、析构函数 或 成员函数,必须在作用域上加 即在::前加泛型类才能表示是 类模板 的函数

2.6 类模板函数 分文件编写

写模板类及其函数时,如果和普通类 类的成员函数一样 一部分写在.h 实现写在 .cpp 中则在使用这个模板类时会出现无法链接的情况

//Person.h
#pragma once
#include
#include
using namespace std;

template
class Person {
public:
	Person(T name);
	void show();
	T name;
};

//Person.cpp
#include"Person.h"

template
Person::Person(T name) {
	this->name = name;
}

template
void Person::show() {
	cout << this->name << endl;
}


//Person_test.cpp
#include"Person.h"

void test() {
	Person p("Tom");
	p.show();
}


int main() {
	test();
}

//报错 无法解析的外部命令

因为类模板的类在调用时才会创建,在test()中调用时查看“Person.h”并没有相关构造函数和成员函数,故报错

//解决办法一 (不推荐)
#include"Person.h" 改为 #include"Person.cpp"

//解决办法二
将 Person.h 和 Person.cpp 写在一起,并更名为 Person.hpp -->  .hpp 文件  别人一看就知道是类模板
#include"Person.hpp"即可
2.7 类模板与 友元
#include
#include

using namespace std;

template
class Person;

//step2 因为全局函数使用了模板类,故模板类得在开头声明
template
void show2(Person p) {
	cout << p.name << endl;
	cout << p.age << endl;
}

template
class Person {
	//全局函数 类内实现
	friend void show(Person p) {
		cout << "name : " << p.name << endl;
		cout << "age : "<(Person p);

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

void test() {
	Person p("Jerry", 1);
	show(p);
}

void test02() {
	Person p("Tom", 2);
	show2(p);
}

int main() {
    test();
	test02();
	return 0;
}
3. STL 学习

  • 容器 和 算法 通过 迭代器进行无缝链接
  • 迭代器在初始学习过程中理解为指针
3.1 STL初识 vector容器

基本数据类型:

#include
#include
#include

using namespace std;

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

void test() {
	vector v;

	//尾插数据
	v.push_back(10);
	v.push_back(20);
	v.push_back(30);



	//通过迭代器访问容器数据
	//方法 1
	vector::iterator itBegin = v.begin();
	vector::iterator itEnd = v.end();

	while (itBegin != itEnd)
	{
		cout << *itBegin << endl;
		itBegin++;
	}

	
	//方法 2
	for (vector::iterator it = v.begin(); it!=v.end();it++ )
	{
		cout << *it << endl;
	}

	//方法 3
	for_each(v.begin(), v.end(), MyPrint);

}
int main() {
	test();
	return 0;
}

自定义数据类型:

#include
#include
using namespace std;

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

void test() {
	vector v;

	v.push_back(Person("aa", 1));
	v.push_back(Person("xx", 2));

	for (vector::iterator it = v.begin(); it != v.end(); it++) {	
		//均可
		cout << (*it).name << "\t" << (*it).age << endl;
		cout << it->name << "\t" << it->age << endl;
	}
}

void test02() {
	vector v;

	Person p1("cc", 1);
	Person p2("ii", 22);

	v.push_back(&p1);
	v.push_back(&p2);

	for (vector::iterator it = v.begin(); it != v.end(); it++) {
		//均可
		cout << (**it).name << "\t" << (**it).age << endl;
		cout << (*it)->name << "\t" << (*it)->age << endl;
	}
}


int main() {

	test02();
	return 0;
}
(*it) 就是vector<> 尖括号内的内容
如 : 
for(vector::iterator it = v.begin;it != v.end();it++){
    cout<<(*it)<
3.2 Vector容器嵌套容器
#include
using namespace std;


void test() {
	vector< vector > v;

	vector v1;
	vector v2;
	vector v3;
	vector 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);


	//注意 (*it) 指代什么
	for (vector>::iterator it = v.begin(); it != v.end(); it++) {
		for (vector::iterator vit = (*it).begin(); vit != (*it).end();vit++) {
			cout << *vit << "\t";
		}
		cout << endl;
	}

}

int main() {
	test();
	return 0;
}
3.3 String 容器

string 本质是一个类,里面管理 char*

3.3.1 初始化 *** 作
string();					//创建一个空字符串 如:string str;
string(const char* s);		//使用字符串初始化
string(const string& str);	//拷贝构造函数,使用一个string对象初始化另一个string对象 
string(int n,char c);		//使用n个字符c初始化

3.3.2 赋值 *** 作


3.3.3 字符串拼接


3.3.4 查找和替换

find 找到返回索引下标 (从0开始),未找到 返回 -1

rfind 从右往左, find 从左往右 ,不过索引下标都是从左往右算的


3.3.5 字符串比较
//两个字符串相同,返回0。


//调用字符串小与被调用字符串,返回-1。


//调用字符串大于被调用字符串,返回1 string a = "nihao"; string b = "nihao"; int s; s = a.compare(b); //整体比较 cout << s << endl; s = a.compare(0, 2, b, 1, 2); //比较"ni" 和 "ih" cout << s << endl;


3.3.6 字符串 中的字符存取

通过以上两种方法 既可以访问,也可以修改

3.3.7 字符串的 插入 和 删除


3.3.8 string 获取子串

string email = "[email protected]";

int pos = email.find("@");

// 注意这里截取的长度 == pos
string userName = email.substr(0,pos);

cout<< userName << endl;	//output zhangsan

3.4 vector 容器

vector 和 数组很相似,也称 单端数组,但 vector 可以动态扩展

vector 的迭代器 是支持随机访问的迭代器

3.4.1 vector 构造函数

注意:第二个函数的区间是 前闭后开


3.4.2 vector 容量和大小

empty();				//为空 返回true
resize(int num); 		//如果比以前小,后面的数据就没了
resize(int num,elem)	//比之前大则用 elem补足空位

3.4.3 vector 插入和删除

注意 以上函数的参数 很多都是迭代器作为参数

3.4.4 vector 数据存取

除了使用迭代器 iterator 来访问数据外 还可以用以下函数进行 *** 作

#include
#include
using namespace std;

void test() {
	vector v;
	for (int i = 0; i < 5; i++) {
		v.push_back(i+1);
	}

	//1. 利用【】 访问vector 数据
	for (int i = 0; i < 5; i++) {
		cout << v[i] << "\t";
	
	}
	cout << endl;

	//2. 利用成员函数 at() 修改和访问
	v.at(2) = 100;
	for (int i = 0; i < 5; i++) {
		cout << v.at(i) << "\t";
	}
	cout << endl;

	//3. 获取第一个元素
	cout<<"First is "<

3.4.5 vector 互换容器
swap(vec);

使用场景:

vector v;
for(int i = 0;i<10000;i++){
    v.push_back(i);
}
cout<(v).swap(v);			//v.capacity() = 3 了


3.4.6 vector 预留空间

减少vector在动态扩展容量时的扩展次数

reserve(int len);	//容器预留len个元素长度,预留位置不初始化,元素不可访问,即不能一用完reserve就给其赋值 ,会报错

使用场景:

#include
#include
using namespace std;

void test() {
	vector v;
	
	int num = 0;			//统计 容器开辟空间的次数 
	int* p = NULL;		
	for (int i = 0; i < 10000; i++) {
		v.push_back(i);

		if (&v[0] != p) {
			p = &v[0];
			num++;
		}
	}
	cout << num << endl;
	
}

int main() {
	test();
	return 0;
}

如果加了 v.reserve(10000);

v.reserve(5000);


3.5 deque 容器

双端数组

  • deque看似连续内存空间实则是由中控器维护地址,所以在头尾均可以插入删除,但是访问时没有vector快,因为根据中控器找地址会慢一点
3.5.1 deque 构造函数

和 vector 原理一样


#include
#include
using namespace std;

void show(const deque p) {
	for (deque::const_iterator it = p.begin(); it != p.end(); it++) {	//如果是const 则迭代器也要该 const_iterator
		//*it = 1000;		//error 不可以修改
		cout << *it << " ";
	}
}

void test() {
	deque d;
	for (int i = 0; i < 10; i++) {
		d.push_back(i);
		d.push_front(i + 10);
	}

	show(d);
	
}

int main() {
	test();
	cout << endl;
	return 0;
}


3.5.2 deque 赋值 *** 作


3.5.3 deque 大小 *** 作

deque 不像vector ,deque 没有容量 capacity 概念


3.5.4 deque 插入和删除


3.5.5 deque 数据存取


3.5.6 sort() 后续学习

对于支持随机访问的迭代器,均可以使用sort()进行排序,默认从小到大

3.6 stack 容器

不允许有遍历行为

3.6.1 stack 常用接口


3.7 queue 容器

不允许有遍历行为

3.7.1 queue 常用接口


3.8 list 容器

list(链表)

是 双向循环链表 图片没有表示循环

3.8.1 list 构造函数


3.8.2 list 赋值和交换


3.8.3 list 大小 *** 作


3.8.4 list 插入和删除

// insert , erase 里面传入的是迭代器
#include
using namespace std;


void Print(const list& p) {
	for (list::const_iterator it = p.begin(); it != p.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

void test() {
	list ak;

	ak.push_back(1);
	ak.push_back(2);
	ak.push_back(3);
	ak.push_back(4);
	Print(ak);				//1 2 3 4

	ak.push_front(10);
	ak.push_front(20);
	ak.push_front(30);
	ak.push_front(40);
	Print(ak);				//40 30 20 10 1 2 3 4

	ak.pop_back();
	Print(ak);				//40 30 20 10 1 2 3 

	ak.pop_front();
	Print(ak);				//30 20 10 1 2 3 

	list::iterator it = --ak.end();		//传入的是 迭代器,当然迭代器位置可以自己调整
	ak.insert(--it, 999);
	Print(ak);				//30 20 10 1 999 2 3

	ak.erase(--(--ak.end()));
	Print(ak);				//30 20 10 1 999 3

	ak.push_back(10);
	ak.push_back(10);
	Print(ak);				//30 20 10 1 999 3 10 10

	ak.remove(10);
	Print(ak);				//30 20 1 999 3

	ak.sort();
	Print(ak);				//1 3 20 30 999
}

int main() {
	test();
	return 0;
}

3.8.5 list 数据存取

由于 list 是链表,内存不连续,所以无法通过 [] / .at() 进行随机访问

// list 的迭代器 
list ak;
list::iterator it = ak.begin();
ak++;
ak--;
--ak;
++ak;
//以上 均可以
//而
ak += 1;
ak = ak + 1;
// 因为不支持随机访问 故任意加减数字都不行,加减1 也不行

3.8.6 list 反转和排序
//反转

#include
#include
using namespace std;

class Person {
public:
	Person(string name) {
		this->name = name;
	}
	string name;
};

ostream& operator<<(ostream& out ,const Person& p) {
	out << p.name;
	return out;
}

void Print(const list& p) {
	for (list::const_iterator it = p.begin(); it != p.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

void test() {
	list p;

	Person p1("Tom");
	Person p2("Tony");
	Person p3("Kimi");

	p.push_back(p1);
	p.push_back(p2);
	p.push_back(p3);

	Print(p);

	p.reverse();
	Print(p);

}
int main() {
	test();

	return 0;
}
//排序

#include
#include
using namespace std;

void Print(const list& p) {
	for (list::const_iterator it = p.begin(); it != p.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

bool Compare(int a, int b) {		//排序从大到小 所用函数
	return a > b;
}

void test02() {
	list p;

	p.push_back(10);
	p.push_back(8);
	p.push_back(40);
	p.push_back(1);

	//所有不支持随机访问迭代器的容器,不可以用标准算法
	//不支持随机访问的容器,内部会提供对应的一些算法
	//sort(p.begin(), p.end());			//error

	p.sort();			//默认 从小到大
	Print(p);
    
    p.sort(Compare);	//从 大到小
	Print(p);
}
int main() {
	test02();

	return 0;
}
//自定义数据类型排序
class Person {
public:
	Person(string name,int age,double height) {
		this->name = name;
		this->age = age;
		this->height = height;
	}
	string name;
	int age;
	double height;
};
ostream& operator<<(ostream& out ,const Person& p) {
	out << "  name =" << p.name << " age = " << p.age << " height = " << p.height << endl;;
	return out;
}

bool ComparePerson(Person& p1, Person& p2) {
	if (p1.age == p2.age)
		return p1.height > p2.height;
	return p1.age < p2.age;
}
void Print(const list& p) {
	for (list::const_iterator it = p.begin(); it != p.end(); it++) {
		cout << *it ;
	}
	cout << endl;
}
void test() {
	list p;

	Person p1("Tomi",1,180.2);
	Person p2("Tony",2,177.4);
	Person p3("Kimi",3,190.9);
	Person p4("xxxx", 3, 200.2);
	Person p5("bbbb", 1, 160.2);

	p.push_back(p1);
	p.push_back(p2);
	p.push_back(p3);
	p.push_back(p4);
	p.push_back(p5);


	Print(p);

	p.reverse();
	Print(p);

	p.sort(ComparePerson);
	Print(p);

}
int main() {
	test();

	return 0;
}

3.9 set / multiset 容器

所有元素都会在插入时自动排序

本质:属于关联式容器,底层结构是二叉树实现

  • set : 不允许容器中有重复的元素
  • multiset:允许容器中有重复的元素
3.9.1 set / multiset 构造和赋值
set s1;

//插入数据,只有insert方式
s1.insert(10);
s1.insert(20);
#include
#include
using namespace std;

void Print(const set& s) {
	for (set::const_iterator it = s.begin(); it != s.end(); it++) {
		cout << *it << " ";
	}
}
void Print(const multiset& s) {
	for (multiset::const_iterator it = s.begin(); it != s.end(); it++) {
		cout << *it << " ";
	}
}

void test() {
	set s;
	s.insert(9);
	s.insert(2);
	s.insert(7);
	s.insert(1);
	s.insert(10);
	s.insert(10);			//插了重复的值,但只会显示一个

	Print(s);
}

void test02() {
	multiset s;
	s.insert(9);
	s.insert(2);
	s.insert(7);
	s.insert(1);
	s.insert(10);
	s.insert(10);			//可以插入重复值

	Print(s);
}

int main() {
	test02();
	return 0;
}

3.9.2 set 大小和交换


3.9.3 set 插入和删除

是按照排序后的顺序进行删除


3.9.4 set 查找 和 统计

对于count 在set 里只会返回0,1,而在multiset 则会返回其他值,因为 set里不允许插入重复数据


3.9.5 set 和 multiset 区别
//set

void test() {
	set s;
	
	//接收 insert 的返回值,查看是否插入成功
	//insert 在这里返回值是:pair::iterator,bool> ,pair是对组类型 有两个值
	pair::iterator, bool> ret = s.insert(9);

	//返回值有两个 想查看第二个,即bool类型值
	if (ret.second) {
		cout << "insert success" << endl;
	}
	else
	{
		cout << "insert fail" << endl;
	}	
	Print(s);				//成功
	
	//再插入 9
	ret = s.insert(9);
	if (ret.second) {
		cout << "insert success" << endl;
	}
	else
	{
		cout << "insert fail" << endl;
	}
	Print(s);				//失败

}
// multiset

void test02() {
	multiset s;
	
	//接收 insert 的返回值,查看是否插入成功
	//insert 在这里返回值是:multiset::iterator
	multiset::iterator ret = s.insert(9);

	//返回值只有1个
	if (*ret) {
		cout << "insert success ,inserted is  " << *ret << endl;
	}
	else
	{
		cout << "insert fail " << *ret << endl;
	}
	Print(s);							//output 9

	//再插入 9
	ret = s.insert(9);
	if (*ret) {
		cout << "insert success ,inserted is " << *ret << endl;
	}
	else
	{
		cout << "insert fail " << *ret << endl;
	}
	Print(s);							//output 9 9
		
	//两次插入均成功
}

3.9.6 pair 对组的创建

void test03() {
	//方法 1
	pair p("Tom", 2);
	cout << p.first << "\t" << p.second << endl;

	//方法 2 
	pair p2 = make_pair("Tom", 2);
	cout << p.first << "\t" << p.second << endl;
}

3.9.7 set 排序

利用 仿函数 改变排序规则

基本数据类型同理:

#include
using namespace std;

class Person {
public:
	Person(int age, int grade) {
		this->age = age;
		this->grade = grade;
	}
	
	int age;
	int grade;
};


class MyCompare {
public:
	bool operator()(Person p1, Person p2) const{		//注意这里 要加const
		if (p1.age == p2.age) {
			return p1.grade > p2.grade;
		}
		return p1.age > p2.age;
	}
};


void Print(const set& p) {
	for (set::iterator it = p.begin(); it != p.end(); it++) {
		cout<< "age = " << (*it).age << "  grade = " << (*it).grade << endl;
	}
	cout << endl;
}

void test() {
	//需要在插入前指定 排序规则,这里我指定的规则在MyCompare类中, 重载了()即仿函数
	set p;

	p.insert(Person(5, 2));
	p.insert(Person(1, 2));
	p.insert(Person(1, 3));
	p.insert(Person(2, 2));
	p.insert(Person(4, 2));

	Print(p);
}

int main() {
	test();

	return 0;
}

3.10 map / multimap 容器
  1. map 中所有元素都是 pair;

  2. pair中第一个元素为 key (键值)起到索引作用,第二个元素为 value(实值);

  3. 所有元素都会根据元素的键值自动排序;

map/ multimap 属于关联式容器,底层结构是用二叉树实现

优点:
可以根据key值快速找到value值;

map \ multimap 区别
map 不允许容器中有重复 key 值元素
multimap 允许容器中有重复 key 值元素

3.10.1 map 构造和赋值
#include
#include
using namespace std;

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

};

void test() {
	map m;

	m.insert(pair(1, Person("yhx", 1)));
	m.insert(pair(2, Person("hq", 1)));

	for (map::iterator it = m.begin(); it != m.end(); it++) {
		cout <<"name = "<< it->second.name << " age = " << it->second.age << endl;
	}

}

int main() {
	test();
	return 0;
}

3.10.2 map 大小和交换


3.10.3 map 插入和删除

//可以利用【】 进行访问 
map m;
m.insert(pair(1,2));
m.insert(make_pair(2,3));

// m:  (1,2), (2,3)
cout<< m[2] << endl;	//output 3	,通过键值 访问

3.10.3 map 查找和统计

void test1() {
	map m;
	m.insert(pair(1, 100));
	m.insert(pair(2, 200));
	m.insert(pair(3, 300));

	map::iterator pos = m.find(3);		//true
	if (pos != m.end()) {
		cout << "info first is " << pos->first << "   info second is " << pos->second << endl;
	}
	else {
		cout << "fail " << endl;
	}


	pos = m.find(4);								//false
	if (pos != m.end()) {
		cout << "info first is " << pos->first << "   info second is " << pos->second << endl;
	}
	else {
		cout << "fail "<< endl;
	}


	m.insert(pair(1, 100));				//无效的插入
	cout << "count is " << m.count(1) << endl;
}

这里思考了 multimap 可以允许有重复 key值 这时find()怎么处理

void test2() {
	multimap m;
	m.insert(pair(1, 100));
	m.insert(pair(2, 200));
	m.insert(pair(3, 300));
	m.insert(pair(3, 400));

	for (map::iterator it = m.find(3); it != m.end(); it++) {
		cout << it->first << "  " << it->second << endl;
	}

	m.insert(pair(1, 100));				//有效的插入
	cout << "count is " << m.count(1) << endl;		//output 2
}	

3.10.4 map 排序

依旧是利用 仿函数 修改排序规则

#include
#include
using namespace std;

class Compare {
public:
	bool operator()( int a, int b) const{
        //降序
		return a > b;
	}
};

void test() {
	multimap m;		//加上 排序的类
	m.insert(pair(4, 100));
	m.insert(pair(5, 200));
	m.insert(pair(7, 3300));
	m.insert(pair(3, 400));
	m.insert(pair(1, 1400));
	m.insert(pair(2, 4000));
	m.insert(pair(6, 4300));

	for (map::iterator it = m.begin(); it != m.end(); it++) {		//从大到小
		cout << it->first << "  " << it->second << endl;
	}
}	


int main() {
	test();
	return 0;
}

3.11 函数对象 = 仿函数

如果一个类将()运算符重载为成员函数,这个类就称为函数对象类,这个类的对象就是函数对象。


函数对象是一个对象,但是使用的形式看起来像函数调用,实际上也执行了函数调用,因而得名。


#include
#include
using namespace std;

class MyAdd {
public:
	int operator()(int v1,int v2) {
		return v1 + v2;
	}
};

void test() {
	MyAdd myadd;
	cout << myadd(10, 20) << endl;	//像函数一样被调用,实则是个类
}
//-----------------------------------------

class MyPrint {
public:
	MyPrint() {
		count = 0;
	}
	int count;						//能保存自己的状态
	void operator()(string test) {
		cout << test << endl;
		count++;
	}
};

void test1() {
	MyPrint myprint;
	myprint("hello");
	myprint("hello");
	myprint("hello");
	cout << myprint.count << endl;
}
//-----------------------------------------

void show(MyPrint& p,string test) {	//可以当函数参数传入
	p(test);
}

void test2() {
	MyPrint myprint;
	show(myprint,"Hello c++");
}

int main() {
	test();
	test1();
	test2();
	return 0;
}

3.12 谓词

概念:

  • ==返回值为bool类型的仿函数==称为谓词

  • 如果operator()接收一个参数,那么叫一元谓词

  • 如果operator()接收两个参数,那么叫二元谓词

    函数参数 提示中 Pred 就是谓词

3.12.1 一元谓词
#include
using namespace std;

class Big {
public:
	bool operator()(int val) {
		return val > 5;
	}
};

void test() {
	vector p;
	for (int i = 0; i < 10; i++) {
		p.push_back(i);
	}


	sort(p.begin(), p.end());
	cout << "未找前:" << endl;
	for (vector::iterator it = p.begin(); it != p.end(); it++) {
		cout << *it << " ";
	}

	cout << "\n找后:" << endl;
	//这里find()_if 需要传入 起始位,终点位 和 函数对象 
	//Big() 是 匿名函数对象
	vector::iterator pos = find_if(p.begin(), p.end(), Big());		
	while (pos != p.end())
	{
		cout << *pos <<  " ";
		pos++;
	}

}

int main() {
	test();

	return 0;
}

3.12.2 二元谓词
class Sort{
public:
	bool operator()(int v1, int v2) {
		return v1 > v2;
	}
};

void test2() {
	vector p;
	p.push_back(40);
	p.push_back(20);
	p.push_back(10);
	p.push_back(70);
	p.push_back(50);
	p.push_back(60);

	cout << "Before sort" << endl;
	for (vector::iterator it = p.begin(); it != p.end(); it++) {
		cout << *it << "  ";
	}

	cout << endl;

	cout << "After sort" << endl;
	sort(p.begin(), p.end(), Sort());
	for (vector::iterator it = p.begin(); it != p.end(); it++) {
		cout << *it << "  ";
	}
}

3.13 内建函数对象

STL 内自带的一些函数对象

#inlude		//头文件
  • 分类:
    算数仿函数

  • 关系仿函数

  • 逻辑仿函数

3.13.1 算数仿函数

#include
#include
using namespace std;

void test() {
	negate n;
	cout << n(20) << endl;
}

void  test2() {
	plus n;		//虽然是加法 但只用传一个参,仿函数参数都是一个参
	cout << n(1.2, 3.4) << endl;
}

int main() {
	test();
	test2();
	return 0;
}
3.13.2 关系仿函数

#include
#include
#include
using namespace std;

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

void test() {
	vector p;

	p.push_back(1);
	p.push_back(3);
	p.push_back(6);
	p.push_back(2);
	p.push_back(4);

	for (vector::iterator it = p.begin(); it != p.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;

	//sort(p.begin(), p.end(), MyGreat());		//匿名 仿函数 MyGreat()
	sort(p.begin(), p.end(), greater());	//等于上面的代码,只是greater是STL写好了的仿函数

	for (vector::iterator it = p.begin(); it != p.end(); it++) {
		cout << *it << " ";
	}
}

int main() {
	test();

	return 0;
}
3.13.3 逻辑仿函数

#include
#include
#include
#include
using namespace std;


void test() {
	vector v;
	v.push_back(true);
	v.push_back(false);
	v.push_back(true);
	v.push_back(false);
	v.push_back(true);

	for (vector::iterator it = v.begin(); it != v.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;

	vector v2;
	v2.resize(v.size());

	//使用 逻辑非 将容器v搬运到v2中
	transform(v.begin(), v.end(), v2.begin(), logical_not());

	for (vector::iterator it = v2.begin(); it != v2.end(); it++) {
		cout << *it << " ";
	}
}

int main() {
	test();

	return 0;
}
3.14 STL常用算法 3.14.1 遍历算法 for_each()
#include
#include
#include
#include
using namespace std;

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

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

void test() {
	vector<int> v;
	for (int i = 0; i < 10; i++) {
		v.push_back(i + 1);
	 }
	for_each(v.begin(), v.end(), Print);	//普通函数放函数名
	cout << endl;

	for_each(v.begin(), v.end(), MyOrder());		//仿函数放匿名对象

}

int main() {
	test();
	return 0;
}
transform()

//源码:
template
Function for_each(InputIterator first, InputIterator last, Function fn)
{
  while (first!=last) {
    fn (*first);
    ++first;
  }
  return fn;      // or, since C++11: return move(fn);
}
#include
#include
#include
#include
using namespace std;

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

class Transform {
public:
	int operator()(int val) {
		return val + 100;		//还可以进行运算
	}
};

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

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

	vector v2;
	v2.resize(10);
	transform(v.begin(), v.end(), v2.begin(),Transform());

	for_each(v2.begin(), v2.end(), Prints());
}

int main() {
	test();
	return 0;
}
3.14.2 查找算法

find()

返回迭代器

//find 查找基本数据类型 返回是迭代器
#include
#include
#include
#include
using namespace std;

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


void test() {
	vector v;
	for (int i = 0; i < 10; i++) {
		v.push_back(i+1);
	}
    //我选择打印地址
	vector::iterator pos = find(v.begin(), v.end(), 5);
	cout << &( * pos ) << endl;
	pos = find(v.begin(), v.end(), 6);
	cout << &(*pos) << endl;

}

int main() {
	test();
	return 0;
}

地址是连续的(int 是4个字节) 证明了 vector 是连续存储 故能随机访问

//find 底层源码
template 
_NODISCARD constexpr _InIt _Find_unchecked1(_InIt _First, const _InIt _Last, const _Ty& _Val, false_type) {
    // find first matching _Val
    for (; _First != _Last; ++_First) {
        if (*_First == _Val) {		//注意:这里对自定义数据类型要判断是否相等 要重载== 运算符
            break;
        }
    }

    return _First;
}
//查找自定义数据类型


#include
#include
#include
#include
#include
using namespace std;

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

class Person {
public:
	Person(string name) {
		this->name = name;
	}
	string getName() {
		return name;
	}
	bool operator==(const Person& p) {
		if (p.name == this->name) {
			return true;
		}
		else
		{
			return false;
		}
	}
private:
	string name;
};


void test() {
	vector v;
	for (int i = 0; i < 10; i++) {
		v.push_back(i+1);
	}
	vector::iterator pos = find(v.begin(), v.end(), 5);
	cout << &( * pos ) << endl;
	pos = find(v.begin(), v.end(), 6);
	cout << &(*pos) << endl;

}

void test2() {
	vector v;
	Person p1("Tom1");
	Person p2("Tom2");
	Person p3("Tom3");
	Person p4("Tom4");

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

	vector::iterator pos = find(v.begin(), v.end(), Person("Tom3"));
	if (pos == v.end()) {
		cout << "fail" << endl;
	}
	else
	{
		cout << (*pos).getName() << endl;
	}
}

int main() {
	test2();
	return 0;
}
find_if()

返回迭代器

#include
#include
#include
#include
#include
using namespace std;

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

class Person {
public:
	Person(string name) {
		this->name = name;
	}
	string getName() {
		return name;
	}
	bool operator==(const Person& p) {
		if (p.name == this->name) {
			return true;
		}
		else
		{
			return false;
		}
	}
	
private:
	string name;
};

class MyCompare {
public:
	bool operator()(Person& p) {		//一元谓词
		return p.getName() == "Tom3";
	}
};


void test() {
	vector v;
	for (int i = 0; i < 10; i++) {
		v.push_back(i+1);
	}
	vector::iterator pos = find(v.begin(), v.end(), 5);
	cout << &( * pos ) << endl;
	pos = find(v.begin(), v.end(), 6);
	cout << &(*pos) << endl;

}

void test2() {
	vector v;
	Person p1("Tom1");
	Person p2("Tom2");
	Person p3("Tom3");
	Person p4("Tom4");

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

	vector::iterator pos = find(v.begin(), v.end(), Person("Tom3"));
	if (pos == v.end()) {
		cout << "fail" << endl;
	}
	else
	{
		cout << (*pos).getName() << endl;
	}

	vector::iterator pos2 = find_if(v.begin(), v.end(), MyCompare());	//只能接收一元谓词
	if (pos2 == v.end()) {
		cout << "fail" << endl;
	}
	else
	{
		cout << (*pos2).getName() << endl;
	}

}

int main() {
	test2();
	return 0;
}
adjacent_find()

返回迭代器

binary_find()

返回 bool

在有序序列才能用

count()

//自定义数据类型

#include
#include
#include
#include
#include
using namespace std;

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

class Person {
public:
	Person(string name) {
		this->name = name;
	}
	string getName() {
		return name;
	}
	bool operator==(const Person& p) {
		if (p.name == this->name) {
			return true;
		}
		else
		{
			return false;
		}
	}
	
private:
	string name;
};

void test() {
	Person p("Jerry");
	Person p2("Tom");
	Person p3("Lucy");

	vector v;
	v.push_back(p);
	v.push_back(p2);
	v.push_back(p3);

	Person pp("Kit");

	int counts = count(v.begin(), v.end(), pp);
	cout << counts << endl;
}



int main() {
	test();
	return 0;
}
count_if()

3.14.3 排序算法

sort()
//自定义数据类型排序

#include
#include
using namespace std;

class Person {
public:
	Person(string name,int age) {
		this->name = name;
		this->age = age;
	}
	string getName() {
		return name;
	}
	int getAge() {
		return age;
	}
	bool operator==(const Person& p) {
		if (p.name == this->name) {
			return true;
		}
		else
		{
			return false;
		}
	}

private:
	string name;
	int age;
};

void Print(Person& val) {
	cout << val.getName() <<"\t" < p2.getAge();
}

class Func {
public:
	bool operator()(Person& a, Person& b) {
		return a.getAge() > b.getAge();
	}
};


void test() {
	Person p("Jerry",1);
	Person p2("Tom",2);
	Person p3("Lucy",4);
	Person p4("xxx", 3);


	vector v;
	v.push_back(p);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);

	sort(v.begin(), v.end(),Func());
	for_each(v.begin(), v.end(), Print);


	sort(v.begin(), v.end(), func);
	for_each(v.begin(), v.end(), Print);

}

int main() {
	test();
	return 0;
}
random_shuffle() 洗牌算法
#include
#include
using namespace std;

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

void test() {
	
	srand((unsigned int)time(NULL));		//随机数发生器

	vector v;
	for (int i = 0; i < 10; i++) {
		v.push_back(i + 1);
	}
	random_shuffle(v.begin(), v.end());
	for_each(v.begin(), v.end(), Print());
}


int main() {
	test();
	return 0;
}
merge()

  • 两个容器必须是 有序的,且顺序要一致,升序就要一起升序
  • 使用算合并完仍然是有序的
reverse()

3.14.4 拷贝和替换算法

copy()

replace()

replace_if()

#include
using namespace std;

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

class Great5 {
public:
	bool operator()(int val) {
		return val >= 5;
	}
};
void test() {
	vector v;
	for (int i = 0; i < 10; i++) {
		v.push_back(i + 1);
	}
	for_each(v.begin(), v.end(), Print());

	replace_if(v.begin(), v.end(), Great5(),999);	//大于5的数字换成999
	cout << endl;

	for_each(v.begin(), v.end(), Print());
}


int main() {
	test();
	return 0;
}

swap()

容器必须是同类型

3.14.5 算数生成算法

accumulate()

#include
using namespace std;

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

	int total = accumulate(v.begin(), v.end(), 0);	//0 是起始值
	cout << total << endl;
}


int main() {
	test();
	return 0;
}
fill()

3.14.6 集合算法

set_intersection()

两个容器必须有序

#include
#include
using namespace std;

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

void test() {
	vector v;
	vector v2;
	
	v.push_back(6);
	v.push_back(7);
	v.push_back(2);
	v.push_back(3);
	v.push_back(5);
	v.push_back(4);

	v2.push_back(1);
	v2.push_back(7);
	v2.push_back(8);
	v2.push_back(9);
	v2.push_back(2);
	v2.push_back(10);
	v2.push_back(11);
	v2.push_back(12);
	v2.push_back(0);

	sort(v.begin(), v.end());
	sort(v2.begin(), v2.end());

	vector vTarget;
	vTarget.resize(min(v.size(), v2.size()));

	vector::iterator itEnd = set_intersection(v.begin(), v.end(), v2.begin(), v2.end(), vTarget.begin());

	for_each(vTarget.begin(), itEnd, MyPrint);  //注意这里 我是以itEnd 结尾的,如果用vTarget.end()结尾,则 会补0输出
}


int main() {
	test();
	return 0;
}
set_union()

#include
#include
using namespace std;

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

void test() {
	vector v;
	vector v2;
	
	v.push_back(6);
	v.push_back(7);
	v.push_back(2);
	v.push_back(3);
	v.push_back(5);
	v.push_back(4);

	v2.push_back(1);
	v2.push_back(7);
	v2.push_back(8);
	v2.push_back(9);
	v2.push_back(2);
	v2.push_back(10);
	v2.push_back(11);
	v2.push_back(12);
	v2.push_back(0);

	sort(v.begin(), v.end());
	sort(v2.begin(), v2.end());

	vector vTarget;
	vTarget.resize(v.size() + v2.size());

	vector::iterator itEnd = set_union(v.begin(), v.end(), v2.begin(), v2.end(), vTarget.begin());
	vTarget.resize(itEnd - vTarget.begin());		//这样可以计算并集大小

	for_each(vTarget.begin(), vTarget.end(), MyPrint);
}


int main() {
	test();
	return 0;
}
set_difference()

差集

#include
using namespace std;

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

void test() {
	vector v;
	vector v2;
	
	v.push_back(6);
	v.push_back(7);
	v.push_back(2);
	v.push_back(3);
	v.push_back(5);
	v.push_back(4);

	v2.push_back(1);
	v2.push_back(7);
	v2.push_back(8);
	v2.push_back(9);
	v2.push_back(2);
	v2.push_back(10);
	v2.push_back(11);
	v2.push_back(12);
	v2.push_back(0);

	sort(v.begin(), v.end());
	sort(v2.begin(), v2.end());

	vector vTarget;
	vTarget.resize(max(v.size(),v2.size()));

	//v1的差集
	vector::iterator itEnd = set_difference(v.begin(), v.end(), v2.begin(), v2.end(), vTarget.begin());
	for_each(vTarget.begin(), itEnd, MyPrint);		//output 3 4 5 6

	cout << endl;

	//v2的差集
	itEnd = set_difference(v2.begin(), v2.end(), v.begin(), v.end(), vTarget.begin());
	for_each(vTarget.begin(), itEnd, MyPrint);		//output 0 1 8 9 10 11 12
}


int main() {
	test();
	return 0;
}
r (int i = 0; i < 10; i++) {
		v.push_back(i + 1);
	}
	random_shuffle(v.begin(), v.end());
	for_each(v.begin(), v.end(), Print());
}


int main() {
	test();
	return 0;
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/568537.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-09
下一篇 2022-04-09

发表评论

登录后才能评论

评论列表(0条)