c++核心编程
const指针
- 常量指针 (指针指向常量) const int *p=&a 指针的指向可以修改,指针指向的值不能修改
- 指针常量 int * const p=&a 指针的指向不能修改,但是指向的值可以修改
内存分区模型
c++程序在执行时,将内存大方向划分为4个区域
- 代码区:存放函数体的二进制代码,由操作系统进行管理的
- 全局区:存放全局变量和静态变量以及常量
- 栈区:由编译器自动分配释放,存放函数的参数值,局部变量等
- 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收
new操作符
堆区开辟的数据,由程序员手动,用delete释放、
new返回是该数据类型的指针 int *p=new int(10); delete p;
释放数组时 delete [] arr;
引用
给变量起别名 int &b=a &别名=原名
引用注意事项
- 引用必须初始化
- 引用在初始化后,不可以改变
引用做函数的返回值
不要返回局部变量的引用 第一次正确是编译器做了保存,第二次就是非法操作了(乱码)
函数的调用可以作为左值 如果函数的返回值是引用,这个函数调用可以作为左值
int& test(){
int a=10;
return a;
}
int& test02(){
static int a=10;//静态变量,存放在全局区,全局上的数据在程序结束后系统释放
return a;
}
int main(){
int &ref=test();
cout<<ref<<endl;//第一次结果正确,是因为编译器做了保留
cout<<ref<<endl;//第二次的结果错误,因为a的内存已经释放
int &ref2=test02();
cout<<ref2<<endl; //不管打印多少次,答案都正确都是10,这是静态变量,要等程序运行完才释放
test02()=1000;//如果函数的返回值是引用,这个函数调用可以作为左值
cout<<ref2<<endl://结果变成了1000,相当远返回了a,则a=1000;
}
引用的本质就是c++内部实现的一个指针常量 指向不能变
常量引用
主要用来修饰形参,防止误操作
//例如
int &ref=10; //错误,应为引用必须是内存空间
const int &ref=10;//正确,编译器将代码修改为 int temp=10; const int & ref=temp; 且这个只能读,不能修改; ref=20就会报错
函数提高
-
函数默认参数
c++中,函数的形参列表中的形参是可以有默认值的,调用函数时没有传入参数就用默认值,如果传入了参数就用传入的参数
int func(int a, int b=20, int c=30){ return a+b+c; }
注意事项
- 如果某个位置已经有了默认参数,那么从这个位置往后,从左到右都必须有默认值。
- 如果函数声明有默认参数,函数实现就不能有默认参数。(声明和实现只能有一个有默认参数,避免二义性)
-
函数占位参数
c++函数的形参列表里可以有占位参数
void func(int a, int ){//第二个参数就是占位参数,在调用的时候,也要传入两个参数。 cout<<a<<endl; } void func(int a, int =10 ){} //占位参数还可以有默认参数
-
函数重载
可以让函数名相同,提高复用性 重载就是参数不一样就行(包括类型,个数,顺序)
函数的返回值不可以作为函数重载的条件(有二义性,不知道调用哪一个)
- 函数重载必须在同一个作用域下、
- 函数名称相同
- 函数参数类型不同,或者个数不同,或者顺序不同。
引用作为重载条件
void fun(int &a){} void fun(const int &a){} //这两个函数编译能通过,参数的类型不一样,一个是int,一个是const int
类和对象
c++中类和struct的区别
- sturct 默认权限为公共
- class默认权限为私有(类中如果不声明访问权限,默认就是private )
例
class Circle{
publiec://访问权限
int r;
double calculateZC(){
return 2*PI*r;
}
}
int main(){
Circle c1;
c1.r=10;
cout<<"圆的周长是:"c1.calculateZC()<<endl;
}
类在设计的时候,可以把属性和行为放在不同的权限下,加以控制
public 公共权限 类内和类外都可以访问
protected 保护权限 只能类内访问 儿子可以访问父亲中的保护内容(继承)
private 私有权限 只能类内访问 儿子不可以访问父亲中的私有内容
class Cube{
public:
set)(){}
get(){}
int caculateS(){
return 2*L*W+2*W*H+2*L*H;
}
int calculateV(){
return L*W*H;
}
bool isSameByClass(Cube &c){
if(L==c.getL() && W==c.getW() && H==c.getH())
return ture;
return false;
}
private:
int L;//长
int W;//宽
int H;//高
}
点和圆关系案例
class POint{
public:
set(){}
get(){}
private:
int x;
int y;
};
class Circule{
public:
void isInCircle(Circle &c, Point &p){
int distanc= //计算两点之间距离 平方
int rDistance= //计算半径的平方;
if(){}//判断关系
}
private:
int r;//半径
Point center;//圆心
};
构造函数和析构函数
- 构造函数:主要作用在于创建对象的成员属性赋值,构造函数由编译器自动调用
- 析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作, 将堆区开辟的数据进行释放草走
构造函数语法:类名(){}
- 没有返回值,也不写void
- 构造函数可以有参数,可以发生重载
析构函数语法:~类名(){}
-
没有返回值,也不写void
-
函数名称与类名相同,在名称前加上符号
-
析构函数不可以有参数,因此不可以发生重
//拷贝构造函数 class Person{ public: Person(const Person &p){ //将传入的人身上的所有属性,拷贝到我身上 age=p.age; } private: int age; }; //构造函数 //显示法 Person p1; Person p2=Person(10); //有参构造 Person p3=Person(p2); //拷贝构造 //隐式转换法 Person p4=10; //相当于写了 Person p4=Person(10) Person p5=p4; //拷贝构造
c++中拷贝构造函数调用时机通常有三种情况
- 使用一个已经创建完毕的对象来初始化一个新对象 Person p2(p1)
- 值传递的方式给函数参数传值 void doWork(Person p){} Person p; doWork§;
- 以值方式返回局部对象
构造函数调用规则
- 默认构造函数(无参,函数体为空)
- 默认析构函数(无参,函数体为空)
- 默认拷贝构造函数,对属性进行值拷贝
构造函数调用规则如下:
- 如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造
- 如果用户敌营拷贝构造函数,c++不会再提供其他构造函数
深拷贝与浅拷贝
浅拷贝:简单的赋值拷贝操作 浅拷贝可能带来堆区的内存重复释放
深拷贝:在堆区重新申请空间,进行拷贝操作
**总结:**如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题。
初始化列表
class Person{
public:
Person(int A, int B, int C){//传统初始化操作
a=A;b=B;c=C;
}
//初始化列表初始化属性
Person(int A, int B, int C):a(A),b(B),c(C)
{
}
int a;
int b;
int c
}
类对象作为类成员
c++类中的成语可以是另一个类的对象,该成员成为对象成员
class Phone{
public:
Phone(string pName){
this.PName=pName;
}
string PName;
}
class Person{
public:
Person(string name, string pName):Name(name),phone(PName){
}
string Name;
Phone phone;
}
当其他类对象作为本类成员,构造时候先构造类对象,再构造自身
静态成员
在成员变量和成员函数前加上关键词static,成为静态成员,
-
静态成员变量
- 所有对象共享同一份数据
- 在编译阶段分配内存
- 类内声明,类外初始化
class Person{ public: static int A; } int Person::A=100; //类内声明,类外初始化操作 //静态成员变量不属于某个对象上,所有对象都共享同一份数据 //通过对象访问静态变量 Person p; p.A; //通过类名进行访问 Person::A //静态成员变量也是有访问权限的 类外不能访问私有的内容
-
静态成员函数
- 所有对象那个共享同一个函数
- 静态成员函数只能访问静态成员变量
//可通过对象和类名进行访问(和访问静态成员变量类似)
//静态成员函数可以访问静态成员变量,不能方位非静态成员变量(无法区分到底是哪个对象的变量)
c++对象模型和this指针
在c++中,类内的成员变量和成员函数分开存储
只有非静态成员变量才属于类的对象上
this指针
c++通过提供特殊的对象指针,this指针指向被调用的成员函数所属的对象
this指针是隐含每一个非静态成员函数内的一种指针
this指针的用途:(和java中的this类似) this->age
- 当形参和成员变量同名时,可用this指针来区分(解决名称冲突)
- 在类的非静态成员函数中返回对象本身,可使用return *this
空指针调用成员函数
const修饰成员函数
常函数:
- 成员函数后加const后我们称为常函数
- 常函数内不可以修改成员属性
- 成员属性声明附加关键字mutable后,在常函数中依然可以修改
class Person{
public:
void showPerson() const
{
this->m_A=100;
}
int m_A;
}
常对象:
- 声明对象前附加const称对象为常对象
- 常对象只能调用常函数
友元
友元的目的就是让一个函数或者类访问另一个类中私有成员 关键字:friend
友元的三种实现
- 全局函数做友元
class Building{
friend void goodGay(Building *building);//声明友元函数
public:
string m_SittingRooome
private:
string m_BedRoom
};
//全局函数
void goodGay(Building *building){
cout<<building->m_SittingRoom<<endl;
}
- 类做友元
class Builidng;
class GoodGay{
public:
GoodGay();
void visit();
Building *building;
}
class Building{
friend class GoodGay;
public:
Building();
public:
string m_SittingRooome
private:
string m_BedRoom
};
Building::Building(){
m_SittingRoom="ss";
m_BedRoom="sf";
}
GoodGay::GoodGay(){
building=new Building;
}
void GoodGay::visit(){
}
- 成员函数做友元
class Builidng;
class GoodGay{
public:
GoodGay();
void visit();
Building *building;
}
class Building{
friend void GoodGay::visit();//成员函数做有缘
public:
Building();
public:
string m_SittingRooome
private:
string m_BedRoom
};
Building::Building(){
m_SittingRoom="ss";
m_BedRoom="sf";
}
GoodGay::GoodGay(){
building=new Building;
}
void GoodGay::visit(){
cout<<building->m_BedRoom;
}
运算符重载
编译器起了一个通用的名称
可通过成员函数和全局函数进行重载
//成员函数重载
Person operator+ (Person &p){
Person temp;
temp.a=this->a+p.a;
temp.b=this_>b+p.b;
return temp;
}
//用法
Person p3=p1.operator+(p2);
//简化为
Person p3=p1+p2;
//全局函数重载
Person operator+ (Person &p1,Person &p2){
Person temp;
temp.a=p1.a+p2.a;
temp.b=p1.b+p2.b;
return temp;
}
//用法
Person p3=operator+(p1,p2);
//简化为
Person p3=p1+p2;
对于内置的数据类型的表达式的运算符是不可能改变的
不要滥用运算符重载
左移
不会利用成员函数重载《 运算符,因为无法实现cout 在左侧
只能利用全局函数重载左移运算符
ostream & operator<<(ostream &cout, Person &p){ //本质 operator<<(cout,p) 简化 cout<<p
cout<<"a="<<p.a<<" b="<<p.b;
return cout;
}
递增运
//前置递增
MyInteger& operator++(){
n++;
return *this;
}
//后置递增
void operator++(int){//int戴白哦占位参数,可以用于区分前置和后置递增
//先记录当时结果、
MyInterger temp=*this;
n++
//最后将记录结果返回
return temp;
}
前置递增返回引用,后置递增返回值
赋值
c++编译器至少给一个类添加4个函数
- 默认构造函数(无参,函数体为空)
- 默认析构函数(无参,函数体为空)
- 默认拷贝构造函数,对属性进行值拷贝
- 赋值运算符operator=对属性进行值拷贝
如果类中有属性指向堆区,做赋值操作时也会出现浅/深拷贝问题
Person& operator(Person &p){
//编译器提供浅拷贝
//应该先判断是否有属性在堆区域,如果有先释放干净,然后再深拷贝
if(m!=NULL){
delete m;
m=NULL;
}
m=new int(*p.m);
return *this;
}
关系
bool operator==(Person &p){
if(this->name==p.name && this.age==p.age){
return true;
}
return false;
}
函数调用
仿函数非常灵活,没有固定写法
class MyAdd{
public:
int operator()(int n1, int n2){
return n1+n2;
}
}
void test02(){
MyAdd myadd;
int ret=myadd(100, 100);
cout<<ret<<endl;
//匿名函数对象
cout<< MyAdd()(100, 100)<<endl;
}
继承
下级别的成员除了拥有上一级别的共性,还有自己的特性。
减少重复代码
语法:class 子类 : 继承方式 父类
继承方式一共三种
- 公共继承
- 保护继承
- 私有继承
父类中的私有属性不管以哪一种方式继承,子类都不能访问
公共继承:父类中的public和protected和子类中的一一对应
保护继承,父类中的public和protected在子类中都变成了protected
私有继承,父类中的public和protected在子类中都变成了private
继承中的对象模型。
父类中所有非静态成员属性都会被子类继承下去,父类中私有成员属性,是被编译器给隐藏了,因此是访问不到。
构造函数
子类继承父类后,当创建子类对象,也会调用父类的构造函数
继承中的构造和析构顺序如下:先构造父类,再构造子类,析构的顺序域构造的顺序相反
总结:继承中先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反
继承同名成员处理方式
当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据?
- 访问子类同名成员:直接访问即可
- 访问父类同名成员:需加作用域
如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有同名成员函数
静态成员和非静态成员出现同名,处理方式一致
//通过对象访问 s继承Base
s.m_A s.Base::m_A
//通过类名访问
s::m_A s::Base::m_A //第一个::代表通过类名方式访问 第二个::代表访问父类作用域下
多继承
语法: class 子类 : 继承方式 父类1,继承方式 父类2 …
多继承可能会引发父类中有同名成员出现,需要加作用域区分
c++实际开发中不建议用多继承
如果多个父类中含有同名属性,则访问的时候需要加作用域来区别
菱形继承
两个派生类继承同一个基类,又有某个类同时继承这两个派生类,这种继承被称为菱形继承,或者钻石继承
利用虚继承 解决菱形继承的问题 继承之前加上关键字 virtual变为虚继承
class Animal{
public:
int age;
}
class Sheep: virtual public Animal{};
class Tub: virtual public Animal{};
class SheepTuo: public Sheep, public Tuo{};
总结:
- 菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义
- 利用虚继承可以解决零星继承问题
多态
- 静态多态:函数重载和运算符重载属于静态多态,复用函数名
- 动态多态:派生类和虚函数实现运行时多态
静态多态和动态多态区别
- 静态多态的函数地址早绑定 ----- 编译阶段确定函数地址 如果不让这个函数地址提前绑定,在运行阶段进行绑定,可以用virtual关键字,让函数变成虚函数
- 动态多态的函数地址晚绑定 ----- 运行阶段确定函数地址
动态多态满足条件
- 有继承关系
- 子类重写父类的虚函数
动态多态使用条件
- 父类的指针或者引用, 指向子类对象
重写:函数返回值类型 函数名 参数列表 完全一致称为重写
多态优点
-
组织结构清晰
-
可读性强
-
对于前期和后期扩展以及维护性高
纯虚函数和抽象类
纯虚函数语法:virtual 返回值类型 函数名 (参数列表)=0;
当类中有了纯虚函数,这个类也称为抽象类
class Base{
public:
virtual void func()=0;//纯虚函数
};
抽象类特点
- 无法实例化对象
- 子类必须重写抽象类中的纯虚函数,否则也属于抽象类
//抽象类,全为抽象方法,用virtual 关键字
class AbstartDrinking{
public:
virtual void Boil()=0;
virtual void Brew()=0;
virtual void PourInCop()=0;
virtual void PutSomething()=0
};
虚析构和纯析构
多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
解决方法:将父类中的析构函数改为虚析构或纯析构
虚析构和纯析构共性
- 可以解决父类指针释放子类对象
- 都需要有具体的函数实现
虚析构和纯析构的区别:如果是纯虚析构,该类属于抽象类,无法实例化对象
纯虚析构 需要声明也需要实现
虚析构语法: virtual ~类名(){}
纯虚析构语法 virtual ~类名(){}=0; 类名::~类名(){}
总结
- 虚析构或纯虚析构就是用来解决通过父类指针释放子类对象
- 如果子类中没有堆区数据,可以不写为虚析构或纯虚析构
- 拥有纯虚析构的类也属于抽象类
文件操作
通过文件可以将数据持久化
c++中对文件操作需要包含头文件
- 文本文件 ASCLL码存
- 、二进制文件 二进制存储
操作文件的三大类
- ofstream:写操作
- ifstream:读操作
- fstream:读写操作
写文件步骤
- 包含头文件 #include
- 创建流对象 ofstream ofs; 写文件是ifstream ifs;
- 打开文件 ofs.open(“文件路径”,打开方式)
- 写数据 ofs<<“写入的数据”
- 关闭文件 ofs.close();
文件打开方式
- ios::in 为读文件而打开文件
- ios::out 为写文件而打开文件
- ios::ate 初试位置:文件尾
- ios::app 追加方式写文件
- ios::trunc 如果文件存在先删除,再创建
- ios::binary 二进制方式
注意:文件打开方式可以配合使用,利用 | 操作符
例如:ios::binary | ios::out
总结:
- 文件操作必须包含头文件ifstream
- 读文件可以利用ofstream,或者fstream类
- 打开文件时需要指定操作文件的路径,以及打开方式
- 利用<<可以向文件中写数据
- 操作完毕,要关闭文件
二进制文件
以二进制的方式对文件进行读写操作
打开方式要指定为ios::binary
写文件
二进制方式写文件主要利用流对象调用成员函数write
函数原型: ostream& write( const char * buffer, int len);
参数解释:字符指针buffer指向内存中一段存储空间。len是读写的字节数
示例:
#include <fstream>
#include <string>
class Person{
public:
char name[64];
int age;
};
osftream ofs;
ofs.open(" ",ios::out | ios::binary);
Person p={"dsfs",120};
ofs.write((const char *)&p, sizeof(p));
ofs.close();
读文件
二进制方式读文件主要利用流对象调用成员函数read
函数原型: istream& read( char *buffer, int len);
参数解释:字符指针buffer指向内存中一段存储空间。 len是读写的字节数
模板
建立通用的模具,提高复用性
函数模板
函数模板
- c++另一种编程思想 泛型编程,利用的就是模板技术
- 两种模板机制:函数模板和类模板
函数模板
建立函数的时候,不具体指定函数的返回类型和参数,用一个虚拟的类型来代替
template<typename T>
函数声明定义
解释:
template 声明创建模板
typenaem 表明其后面的符号是一种数据类型,可以用class代替
T 通用的数据类型,名称可以替换,通常用大写字母
//定义函数模板
template<typename T>
//声明一个模板,告诉编译器后面代码中紧跟着的T不要报凑,T是一个通用数据类型,typename 可以替换成class
void myWamp(T &a, T &b){
T temp=q;
a=b;
b=temp;
}
int a,b;
//自动类型推导
mywamp(a,b);
//显示指定类型
myWamp<int>(a,b);
总结:
- 函数模板利用关键字template
- 使用函数模板有两种方式:自动类型推导、显示指定类型
- 模板的目的是为了提高复用性,将类型参数化
普通函数与函数模板区别:
- 普通函数调用时可以发生自动类型转换(隐式类型转换)
- 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
- 如果利用显示指定类型的方式,可以发生隐式类型转换
普通函数与函数模板的调用规则
调用规则如下:
- 如果函数模板和普通函数都可以实现,优先调用普通函数
- 可以通过空模板参数列表来强制调用函数模板
- 函数模板也可以发生重载
- 如果函数模板可以产生更好的匹配,优先调用函数模板
模板也有局限性,有些特定数据类型,需要具体化方式做特殊实现
template <typename T>
bool compare(T &a, T &b){
if(a==b)
return ture;
return false;
}
//具体化模板
template<>
bool compare(person &a, person &b){
if(a.name == b.name && a.age==b.age)
return true;
return false;
}
总结
- 利用具体化的模板,可以解决自定义类型的通用化
- 学习模板并不是为了写模板,而是在STL能够运用系统提供的模板
类模板
例
template<class nameType, class ageType>
class person{
public:
person(nameType name, ageType age){
this->age=age;
this->name=name;
}
priavate:
nameType name;
ageType age;
}
类摸吧和函数模板的区别
- 类模板没有自动类型推导的使用方式
- 类模板在模板参数列表中可以有默认参数
template<class nameType, class ageType=int>
class person{
public:
person(nameType name, ageType age){
this->age=age;
this->name=name;
}
priavate:
nameType name;
ageType age;
}
Person<string>("fsdfs",111);//这样正确,前面定义了类可以有默认参数,这里的第二个就默认int类型
类模板中成员函数创建时机
类模板中成员函数和普通类中成员函数创建时机是有区别的:
- 普通类中的成员函数一开始就可以创建
- 类模板中的成员函数在调用时才创建
类模板对象做函数参数
- 指定传入的类型 直接显示对象的数据类型 常用
- 参数模板化 将对象中的参数变为模板进行传递
- 整个类模板化 将这个对象类型模板化进行传递
template<class nameType, class ageType=int>
class person{
public:
person(nameType name, ageType age){
this->age=age;
this->name=name;
}
priavate:
nameType name;
ageType age;
}
//指定传入类型,这是最常用的一种
void printPerson(Person<stirng, int> &p){
//cout person
}
//参数模板化
tempate<class T1, class T2>
void printPerson2(person<T1, T2>&P){
//cout person
}
//整个类模板化
template<calss T>
void printPerson3(T &p){
//cout person
}
int main(){
person<string, int>p("dsd",23);
printperson(p);
printperson2(p);
printperson3(p);
}
类模板与继承
类模板碰到继承时,注意一下几点
- 当子类继承的父类是一个类模板时,子类在声明的时候,要指定父类中T的类型
- 如果不指定,编译器无法给子类分配内存
- 如果想灵活指定父类中T的类型,子类也需变为类模板
template<class T>
class Base{
T m;
Base(T );
}
class Son : public Base<int>{//正确继承
}
template<calss T>
class son: public Base<T>{
T m;
}
//类模板中的函数类外实现
template<class T1)
Base::Base(T a){
m=a;
}
类模板分文件编写
类模板中成员函数创建时机是在调用阶段,导致分文件编写时连接不到
解决方案
- 直接包含.cpp源文件
- 将声明和实现写到同一个文件中,并更改后缀名为.hpp, hpp是约定的名称,并不是强制
STL
目的就是提升代码复用性
- STL( standard template livrary)标准模板库
- STL从广义上分为,容器、算法、迭代器
- 容器和算法之间通过迭代器进行无缝连接
- STL几乎所有的代码都采用了模板类或模板函数
STL六大组件、
容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器
- 容器:各种数据结构,如vectro、list、deque、set、map等
- 算法:各种常用的算法,如sort、find、cop、for_each等
- 迭代器:扮演了容器与算法的胶合剂
- 仿函数:行为类似函数,可作为算法的某种策略
- 适配器:一种用来修饰容器或者仿函数或迭代器接口的东西
- 空间适配器:负责空间的配置与管理
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void myprint(int value){
cout<<value;
}
void test01(){
vector<int> arr;
arr.push_back(10);
arr.push_back(5);
arr.push_back(8);
//指向第一个元素
// vector<int>::iterator itBegain=arr.begin();
// //指向最后一个元素的下一个位置
// vector<int>::iterator itEnd=arr.end();
for(vector<int>::iterator i=arr.begin(); i<arr.end(); i++)
cout<<*i<<" ";
cout<<endl;
//利用STL提供的标准算法
for_each(arr.begin(),arr.end(), myprint);
}
int main(){
printf("hello world\n");
test01();
system("pause");
return 0;
}
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;
class Person{
public:
Person(string name, int age){
this->name=name;
this->age=age;
}
string name;
int age;
};
void test(){
vector<Person> persons;
Person p1("fee",10);
Person p2("sdf",14);
Person p3("eef",1);
Person p4("fdge",12);
Person p5("gr",8);
persons.push_back(p1);persons.push_back(p2);persons.push_back(p3);
persons.push_back(p4);persons.push_back(p5);
// 遍历数据
cout<<(persons.begin())->name<<endl;
for(vector<Person>::iterator i=persons.begin(); i<persons.end(); i++){
cout << "姓名:" << i->name << " 年龄:" << i->age << endl;
}
}
int main(){
test();
return 0;
}
二维数组
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;
void test(){
vector<vector<int>> v;
vector<int>v1;
vector<int>v2;
vector<int>v3;
vector<int>v4;
//向小容器中添加数据
for(int i=0; i<4; i++){
v1.push_back(i+1);
v2.push_back(i+2);
v3.push_back(i+3);
v4.push_back(i+4);
}
//将小容器插入到大容器中
v.push_back(v1);v.push_back(v2);v.push_back(v3);v.push_back(v4);
//通过大容器,把所有数据遍历以便
for(vector< vector<int>>::iterator it=v.begin(); it!=v.end(); it++){
//(*it)------容器vector<int>
for(vector<int>::iterator vit=(*it).begin(); vit!=(*it).end(); vit++){
cout<<*vit<<" ";
}
cout<<endl;
}
}
int main(){
test();
return 0;
}
string容器
string是c++风格的字符串,而string本质上是一个类
string 和char *的区别
- char *是一个指针
- string是一个类,类内部封装了char*,管理这个字符串,是一个char*型的容器
string构造函数
- string(); //创建一个空字符串,例如 string str;
- string(const char* s); //使用字符串s初始化
- string(const string& str); //使用一个string对象初始化另一个string对象
- string(int n, char c); //使用n个字段c初始化;
string s1;//默认构造
const char *str="hello world";
string s2(str);
string s3(s2);
string s4(10,'q');
string赋值操作
string str1;
str1="hello world";
string str2;
str2=str1;
string str3;
str3='s';//将字符赋值给当前的字符出纳
string str4;
str4.assign("hello c++");//将字符串赋值给当前的字符串
string str5;
str5.assign("hello c++",5);//将字符串的前几个字符赋值给str5;
string str6;
str6.assign(str5);//将str5赋值给str6;
string str7;
str7.assign(10,'w');//将10个w字符赋值给str7;
}
总结:string的赋值方式很多, operator= 这种方式是比较实用的
string字符拼接
实现在字符串末尾拼接字符串
函数原型
- string& operator+=(const char* str); //重载+=操作符
- string& operator+=(const char c); //重载+=操作符
- string& operator+=(const string& str); //重载+=操作符
- sting& append(const char *s); //把字符串s连接到当前字符串末尾
- string& append(const char *s, int n); //把字符串s的前n个字符连接到当前字符串末尾
- string& append(const string &s); //同operator+=(const string& str);
- string& append(const string &s, int pos, int n); //字符串s中从pos开始的n个字符连接到字符串结尾
string查找和替换
函数原型
- int find(const string& str, int pos=0) const; //查找str第一次出现位置,从pos开始查找
- int find(const char *s, int pos=0) const; //查找s第一次出现位置,从pos开始查找
- int find(const char* s, int pos, int n) const; //从pos位置查找s的前n个字符第一次位置
- int find(const char c, int pos=0) const; //查找字符c第一次出现位置
- int rfind(const char* s, int pos=npos )const; //查找str最后一次位置,从pos开始查找
- int rfind(const string& str, int pos=npos)const; //查找str最后一次位置,从pos开始查找
- int rfind(const char *s, int pos, int n)const; //从pos查找s的前n个字符最后出现位置
- int rfind(const char c, int pos=0)const; //查找字符cc最后一次出现位置
- string& replace(int pos, int n, const string& str) //替换从pos开始n个字符为字符串str
- string& replace(int pos, int n, const char* s); //替换从pos开始的n个字符为字符串s
总结
- find查找是从左往右,rfind从右往左
- find找到字符串后返回查找的第一个字符位置,找不到返回-1
- replace在替换时,要指定从哪个位置起,多少个字符,替换成什么样的字符串
string字符串比较
字符串比较是按字符的ASCII码进行对比
=返回0 >返回1 <返回-1
- int compare(const string& s)const; //与字符串s比较
- int compare(const char *s)const; //与字符串s比较
string字符存取
string中单个字符存取方式有两种
- char& operator[](int n); //通过[]方式取字符
- char& at(int n); //通过at方法获取字符
string str="hello";
for(int i=0; i<str.size(); i++){
cout<<str[i]<<" "
}
cout<<endl;
for(int i=0; i<str.size(); i++){
cout<<str.at(i)<<" "
}
cout<<endl;
string插入和删除
函数原型:
- sstring& insert(int pos, const char*s); //插入字符串
- string& insert(int pos, const string& str); //
- string& insert(int pos, int n, char c); //在指定位置插入n个字符c
- string& erase(int pos, int n=npos); //删除从pos开始的n个字符
string子串
string substr(int pos=0, int n=npos)const; //返回由pos开始的n个字符组成的字符串
string str="abcdef";
string subStr=str.substr(1,3);
cout<<subStr<<endl;//输出结果是bcd;
vector容器
数组是静态空间,而vector可以动态扩展
并不是在原空间之后续接新空间,而是找更大的内存空间,然后将原数据拷贝新空间,释放原空间
vector容器的迭代器是支持随机访问的迭代器
函数原型
- vector v; //采用模板实现类实现,默认构造函数
- vector(v.begin(), v.end()); //将v[begin(),end()]区间中的元素拷贝给本身
- vector( n, elem ); //构造函数将n个elem(值)拷贝给本身
- vector( const vector &vec); //拷贝构造函数
vector容量和大小
函数原型:
- empty(); //判断容器是否为空 为空返回true
- capacity(); //容器的容量 现在总共有多少个空间(动态扩展)
- size(); //返回容器中元素的个数
- resize(int num); //重新指定容器的长度为num,若容器变长,则以默认值填充新位置,变短则删除
- resize(int num, elem); //重新指定容器的长度为num,若容器边长,则以elem填充新位置,变短则删除
vector插入和删除
函数原型:
- push_back(ele); //尾部插入元素
- pop_back(); //删除最后一个元素
- insert( const_iterator pos, ele ); //迭代器指向位置Pos插入元素ele
- insert( const_iterator pos, int cout, ele) ; //迭代器指向位置pos插入count个元素ele
- erase( const_iterator pos); //删除迭代器指向的元素
- insert( const_iterator start, iconst_iterator_end); //删除迭代器从starta到end之间的元素
- clear(); //删除容器中所有元素
vector数据存取
函数原型:
- at(int idx); //返回索引idx所致的数据
- operator[]; //返回索引Indx所指的数据
- front(); //返回容器中第一个数据元素
- back(); //返回容器中最后一个数据元素
vector互换容器
swap(vec); //将vec与本身的元素互换
利用swap可以收缩内存空间
vector<int> v1;
vector<int> v2;//两个已经定义好了的vector
v1.swap(v2); //两个vector中的数据互换
vector预留空间
减少vector在动态扩展容量时的扩展次数
reserve(int len); //容器预留Len个元素长度,预留位置不初始化,元素不可访问
总结:如果数据量较大,可以一开始利用reserve预留空间
deque容器
双端数组,可以对头端进行插入删除操作
deque与vector的区别
- vector对于头部的插入和删除效率较低(要移动元素)
- deque相对而言,对头部的插入删除速度比vector快
- vector访问元素时的速度会比deque快,这和两者内部实现有关
deque内部工作原理
deque内部有个中控器,维护每段缓冲区中的内容,缓冲区中存放真实数据
中控器维护的是每个缓冲区的地址,使得使用deque时像一片连续的内存空间
deque赋值操作
函数原型:
- deque& operator=(const deque &deq); //重载等号操作符
- assign( beg, end); //将【beg,end】区间中的数据拷贝赋值给本身
- assign( n, elem ); //将n个elem拷贝赋值给本身
deque大小操作
函数原型:
- deque.empty(); //判断容器是否为空
- deque.size(); //返回容器中元素的个数
- deque.resize(num); //重新指定容器的长度为num,变长则以默认值填充,反之删除
- deque.resize( num, elem); //重新指定容器的长度为num,变长则以ele填充,反之删除
总结:deque没有容量的概念
deque插入和删除
函数原型
两端插入操作:
- push_back( elem ); //在容器尾部添加一个数据
- push_front( elem ); //在容器头部插入一个数据
- pop_back( ); //删除容器最后一个数据
- pop_front(); //删除容器第一个数据
指定位置操作
- insert( pos, elem ); //在pos位置插入一个elem元素的拷贝,返回新数据的位置
- insert( pos, n, elem ); //在pos位置插入n个elem数据,无返回值
- insert( pos, beg, end ); //在pos位置插入【beg,end】区间的数据,无返回值
- clear(); //清空容器的所有数据
- erase( beg, end ); //删除【beg,end】区间的数据,返回下一个数据的位置
- erase( pos ); //删除Pos位置的数据,返回下一个数据的位置
deque数据存取
函数原型:
- at( int index); //返回索引index所指的数据
- operator[]; //返回索引index所指的数据
- front(); //返回容器中第一个数据元素
- back(); //返回容器中最后一个数据元素
dque排序操作
利用算法实现对deque容器进行排序
sort( iterator beg, iterator end); //对beg和end区间内元素进行排序
stack容器
先进后出(栈)只有一个出口
构造函数
- stack stk; //stack采用模板类实现,stack对象的默认构造形式
- stack( const stack &stk ); //拷贝构造函数
赋值操作:
- stack& operator=( const stack &stk ); //重载等号操作符
数据存取:
- push( elem ); //向栈顶添加元素
- pop(); //从栈顶移除第一个元素
- top(); //返回栈顶元素
大小操作:
- empty(); //判断对战是否为空
- size(); //返回栈的大小
queue容器
先进先出,队列,它有两个出口(一端进,一端出)
构造函数:
- queue que; //queue采用模板类是心啊,queue对象的默认构造形式
- queue( const queue &que); //拷贝构造函数
赋值操作:
- queue& operator=( const queue &que); //重载等号操作符
数据存取
- push(elem); //往队尾添加元素
- pop(); //从对头移除一个元素
- back(); //返回最后一个元素
- front(); //返回第一个元素
大小操作:
- empty(); //判断堆栈是否为空
- size(); //返回栈的大小3
list容器
就是链表(双向循环链表)
函数原型:
- list lst; //lst采用模板类实现,对象的默认构造形式
- list(beg,end); //构造函数将【beg,end】区间中的元素拷贝给本身
- list( n, elem ); //构造函数将n个slem拷贝给本身
- list( const list &lst); //拷贝构造函数
list赋值和交换
函数原型
- assign( beg, end); //将【beg, end】区间中的数据拷贝赋值给本身
- assign( n, elem ); //将n个elem拷贝赋值给本身
- list& operator( const list &lst); //重载等号操作符
- swap( lst ); //将lst与本身的元素互换;
list大小操作
函数原型:
- size(); //返回容器中元素的个数
- empty(); //判断容器是否为空
- resize( num ); //重新指定容器的长度为Num, 若容器边长,以默认值填充,反之删除
- resize( num, elem ); /重新指定容器的长度为Num, 若容器边长,以elem填充,反之删除
list插入和删除
函数原型:
- push_back( elem ); //在容器尾部加入一个元素
- pop_back(); //删除容器中最后一个元素
- push_front( elem ); //在容器开头插入一个元素
- pop_front(); //从容器开头移除第一个元素
- insert( pos, elem ); //在Pos位置插elem元素的拷贝,返回新数据的位置
- insert( pos, n, elem ); //在pos位置插入n个elem数据,无返回值
- insert( pos, beg, end); //从Pos位置插入【beg,end】区间的数据,无返回值
- clear(); //移除容器的所有数据
- erase( beg, end); //删除【beg,end】区间的数据,返回下一个数据的位置
- erease( pos ); //删除pos位置的数据,返回下一个数据的位置
- remove( elem ); //删除容器中所有与elem值匹配的元素
list数据存取
函数原型:
- front()l //返回第一个元素
- back(); //返回最后一个元素
list反转和排序
函数原型:
- reverse(); //反转链表
- sort(); //链表排序
list<int> L;//假设已经有了数据
L.reverse();//反转
L.sort();//排序,默认从小到大,升序
//要让sort从大到小
bool myCompare(int v1, int v2){
return v1>v2;
}
L.sort(myCompare);//排序,从大到小了,降序
排序案例
#include <iostream>
#include <list>
#include <string>
using namespace std;
class Person{
public:
Person(string name, int age, int high){
this->name=name;
this->age=age;
this->high=high;
}
string name;
int age;
int high;
};
bool comparePersonAge(Person &p1, Person &p2){
if(p1.age==p2.age)//年龄相同则身高降序
return p1.high>p2.high;
return p1.age>p2.age;
}
bool comparePersonHigh(Person &p1, Person &p2){
return p1.high>p2.high;
}
void test(){
list<Person> L;
Person p1("dfsfe",2,35);
Person p2("sifhs",7,34);
Person p3("dfs",5,36);
Person p4("dsifho",4,65);
L.push_back(p1);L.push_back(p2);L.push_back(p3);L.push_back(p4);
for(list<Person>::iterator it=L.begin(); it!=L.end(); it++){
cout<<it->name<<" "<<it->age<<" "<<it->high<<endl;
}
cout<<"按照年龄排序后的效果"<<endl;//自定义类型一定要自定义排序规则
L.sort(comparePersonAge);
for(list<Person>::iterator it=L.begin(); it!=L.end(); it++){
cout<<it->name<<" "<<it->age<<" "<<it->high<<endl;
}
cout<<"按照身高排序后的效果"<<endl;
L.sort(comparePersonHigh);
for(list<Person>::iterator it=L.begin(); it!=L.end(); it++){
cout<<it->name<<" "<<it->age<<" "<<it->high<<endl;
}
}
int main(){
test();
cout<<"hello world"<<endl;
}
总结:
- 对于自定义数据类型,必须要指定排序规则,否则百年一起不知道如何进行排序
- 高级排序只是在排序规则上再进行一次逻辑规则指定,并不复杂
set容器
所有元素都会在插入时自动被排序
set/ultiset属于关联式容器,底层结构是二叉树
set和multiset区别
- set不允许容器中有重复的元素,(插入数据用inset)
- set插入数据的同时会返回结果,表示插入是否成功
- multiset允许容器中有重复的元素, ( multiset 不会检测数据)
构造函数
-
set st; //默认构造函数
-
set( const set &st); //拷贝构造函数
-
set& operator=( const set &st); //重载等号操作符
set大小和交换
函数原型:
- size(); //返回容器中元素的数目
- empt(); //判断容器是否为空
- swap(st); //交换两个集合容器
set插入和删除
函数原型:
- insert(elem); //在容器中插入元素 返回值是 pair< set::iterator, bool>
- clear(); //清除所有元素
- erase(pos); //删除pos迭代器所指的元素,返回下一个元素的迭代器
- erase( beg, end); //删除区间【beg,end】的所有元素,返回下一个元素的迭代器
- erase(elem); //删除容器中值为elem的元素
set查找和统计
函数原型:
- find( key ); //查找key是否存在,返回该键的元素的迭代器; 若不存在,返回set.end();
- count( key ); //统计Key的元素个数
pair队组的创建
承兑出现的数据,利用队组可以返回两个数据
- pair< type, type> p(value1, value2);
- pair<type, type> p=make_pair(value1, value2);
set容器排序
set容器默认排序规则为从小到大,利用仿函数,可以改变排序规则
放内置数据类型:
#include <iostream>
#include <set>
using namespace std;
class MyCompare{
public:
bool operator()(int v1, int v2){
return v1>v2;
}
};
void test(){
set<int> s1;
s1.insert(10);s1.insert(40);s1.insert(20);s1.insert(30);s1.insert(16);
for(set<int>::iterator it=s1.begin(); it!=s1.end(); it++){
cout<<*it<<" ";
}
cout<<endl;
//指定排序规则为从大到小
set<int, MyCompare> s2;
s2.insert(10);s2.insert(40);s2.insert(20);s2.insert(30);s2.insert(16);
for(set<int, MyCompare>::iterator it=s2.begin(); it!=s2.end(); it++){
cout<<*it<<" ";
}
}
int main(){
test();
cout<<endl<<"hello world"<<endl;
return 0;
}
放自定义类型数据( 必须指定排序规则 )
#include <iostream>
#include <set>
#include <string>
using namespace std;
class Person{
public:
string name;
int age;
Person(string name, int age){
this->name=name;
this->age=age;
}
};
class comparePerson{
public:
bool operator()(const Person&p1, const Person&p2){
return p1.age>p2.age;
}
};
void test(){
//自定义数据类型都要自己指定排序规则
set<Person,comparePerson> s;
Person p1("sdfs",2);Person p2("df",3);Person p3("fdsf",6);Person p4("fsdgd",9);
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->name<<" "<<it->age<<endl;
}
}
int main(){
test();
cout<<endl<<"hello world"<<endl;
return 0;
}
map容器
map/multimap属于关联式容器,底层结构是二叉树
- map中所有元素都是pair
- pair中第一个元素为key( 键值), 起到索引作用,第二个元素为value (实值)
- 所有元素都会根据元素的键值自动排序
map和multimap区别
- map不允许容器中有重复key值元素
- multimap允许容器中有重复key值元素
构造函数
- map< T1, T2> mp; //map默认构造函数
- map(const map &map); //拷贝构造函数
赋值: map& operator(const map &map); //重载等号操作符
#include <iostream>
#include <map>
using namespace std;
void printMap(map<int, int>&m){
for(map<int, int>::iterator it=m.begin(); it!=m.end(); it++){
cout<<it->first<<" "<<it->second<<endl;
}
}
void test(){
map<int, int> m;
m.insert(pair<int,int>(1,10));
m.insert(pair<int,int>(2,20));
m.insert(pair<int,int>(3,30));
m.insert(pair<int,int>(4,40));
printMap(m);
}
int main(){
test();
return 0;
}
map大小和交换
- size(); //返回容器中元素的数目
- empty(; //判断容器是否为空
- swap(st); //交换两个集合容器
map插入和删除
- insert(elem); //往容器中插入元素
- clear(); //清楚所有元素
- erase(pos); //删除pos迭代器所指元素,返回下一个元素的迭代器
- erase( beg, end); //删除区间[beg, end]的所有元素,返回下一个元素的迭代器
- erease(key); //删除容器中值为key的元素
map<int,int>m;
m.insert(pair<int,int>(1,10));
m.insert(make_pair(2,20));
m.insert(map<Int,int>::value_type(3,30));
m[4]=40;
map查找和统计
- find(key); //查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end();
- count(key); //统计Key的元素个数
map容器排序
利用仿函数,改变排序规则
#include <iostream>
#include <map>
using namespace std;
class MyCompare{
public:
bool operator()(int v1, int v2){
return v1>v2;
}
};
void printMap(map<int, int, MyCompare>&m){
for(map<int, int, MyCompare>::iterator it=m.begin(); it!=m.end(); it++){
cout<<it->first<<" "<<it->second<<endl;
}
}
void test(){
map<int, int, MyCompare> m;
m.insert(pair<int,int>(1,10));
m.insert(pair<int,int>(2,20));
m.insert(make_pair(3,30));
m.insert(make_pair(4,40));
printMap(m);
}
int main(){
test();
return 0;
}
总结:
- 利用仿函数可以指定map日供气的排序规则
- 对于自定义数据类型,map必须指定排序规则,同set容器
函数对象
重载函数调用操作符的类,其对象常称为函数对象
函数对象使用重载()时,行为类似函数调用,也叫仿函数
函数对象(仿函数)是一个类,不是一个函数
谓词
- 返回bool类型的仿函数称为谓词
- 如果operator()接受一个参数,叫做一元谓词
- 。。。。。。两个参数。。,二元谓词
内建函数对象
STL内建了一些函数对象
算数仿函数
实现四则运算; 其中negate是一元运算
- template T plus //+
- template T minus //-
- template T multiplies
- template T divides
- template T modulus //取模
- template T negate //取反
关系仿函数
实现关系对比
- template bool equal_to
- template bool not_equal_to
- template bool greater //大于
- template bool greater_equal
- template bool less //小于
- template bool less_equal
逻辑仿函数
实现逻辑运算
- template bool logical_and
- template bool logical_or
- template bool logical_not
常用算法
遍历
for_each
vector<int> v;
class myprint{
public:
void operator()(int val){
cout<<val<<" ";
}
};
for_each(v.begin(), v.end(), myprint());//要放入仿函数对象
transform
搬运容器到另一个容器中
- transform( iterator beg, iterator end, iterator beg2, iterator end3 , func); ///func为仿函数
查找
- find //查找元素
- find_if //按条件查找元素
- adjacent_find //查找相邻重复元素
- binary_search //二分查找法
- count //统计元素个数
- count_if //按条件统计元素个数
find
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;
void test(){
vector<int> v;
for(int i=0; i<10; i++){
v.push_back(i);
}
vector<int>::iterator it=find(v.begin(),v.end(), 50);
if(it==v.end()){
cout<<"fffffffffff";
}else{
cout<<"ssssssss";
}
}
class Person{
public:
string name;
int age;
Person(string name, int age){
this->name=name;
this->age=age;
}
bool operator==(const Person &p){
if(this->name==p.name && this->age==p.age)
return true;
else
return false;
}
};
void test2(){
vector<Person> v;
Person p1("sfds",12);Person p2("fsf",3); Person p3("fsgwe",6);Person p4("gef",19);
v.push_back(p1);v.push_back(p2);v.push_back(p3);v.push_back(p4);
vector<Person>::iterator it=find(v.begin(),v.end(),p2);
if(it!=v.end()){
cout<<it->name<<" "<<it->age;
}else{
cout<<"not find"<<endl;
}
}
int main(){
test();
test2();
return 0;
}
find_if( iterator beg, iterator end, pred);// pred函数或谓词(返回bool类型的仿函数)
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;
class Person{
public:
string name;
int age;
Person(string name, int age){
this->name=name;
this->age=age;
}
};
class GreaterFive{
public:
bool operator()(int val){
return val>5;
}
};
class Greater20{
public:
bool operator()(const Person &p){
return p.age>20;
}
};
void test(){
vector<int> v;
for(int i=0; i<10; i++){
v.push_back(i);
}
vector<int>::iterator it=find_if(v.begin(), v.end(), GreaterFive());
if(it==v.end())
cout<<"not find"<<endl;
else
cout<<*it;
}
void test2(){
vector<Person>v;
Person p1("sdf",12);Person p2("fsf",4);Person p3("fshgure",5);Person p4("fifjs",82);
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<<it->name<<" "<<it->age<<endl;
else
cout<<"not find"<<endl;
}
int main(){
test();
test2();
return 0;
}
查找相邻重复元素 adjacent_find( iterator beg, iterator end);
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;
void test(){
vector<int> v;
v.push_back(0);v.push_back(2);v.push_back(5);v.push_back(0);v.push_back(3);v.push_back(6);
v.push_back(4);v.push_back(4);
vector<int>::iterator it=adjacent_find(v.begin(), v.end());
if(it!=v.end()){
cout<<*it<<endl;
}else{
cout<<"not find\n";
}
}
int main(){
test();
return 0;
}
查找指定的元素是否存在(二分查找) bool binary_search( iterator beg, iterator end, value);
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;
void test(){
vector<int> v;
for(int i=0; i<10; i++)
v.push_back(i);
if(binary_search(v.begin(), v.end(),5))
cout<<"find value\n";
else
cout<<"not find\n";
}
int main(){
test();
return 0;
}
统计元素个数 count( iterator beg, iterator end, value);
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;
class Person{
public:
Person(string name, int age){
this->name=name;
this->age=age;
}
string name;
int age;
bool operator==(const Person &p){
return this->age==p.age;
}
};
void test(){
vector<int> v;
for(int i=0; i<10; i++)
v.push_back(i);
v.push_back(8);
cout<<count(v.begin(), v.end(), 8)<<endl;
}
void test2(){
vector<Person> v;
Person p1("fds",2);Person p2("fsdf",4);Person p3("fdsf",6);Person p4("fsd",5);
v.push_back(p1);v.push_back(p2);v.push_back(p3);v.push_back(p4);
Person p("dfsf",4);
cout<<count(v.begin(), v.end(),p)<<endl;
}
int main(){
test();
test2();
return 0;
}
按条件统计元素个数 count_if( beg, end, pred);//pred为谓词
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;
class Person{
public:
Person(string name, int age){
this->name=name;
this->age=age;
}
string name;
int age;
};
class Greater5{
public:
bool operator()(int val){
return val>5;
}
};
class AgeGreater2{
public:
bool operator()(const Person &p){
return p.age>2;
}
};
void test(){
vector<int> v;
for(int i=0; i<10; i++)
v.push_back(i);
v.push_back(8);
cout<<count_if(v.begin(), v.end(), Greater5())<<endl;
}
void test2(){
vector<Person> v;
Person p1("fds",2);Person p2("fsdf",4);Person p3("fdsf",6);Person p4("fsd",5);
v.push_back(p1);v.push_back(p2);v.push_back(p3);v.push_back(p4);
Person p("dfsf",4);
cout<<count_if(v.begin(), v.end(),AgeGreater2())<<endl;
}
int main(){
test();
test2();
return 0;
}