c++learn

c++核心编程

const指针

  1. 常量指针 (指针指向常量) const int *p=&a 指针的指向可以修改,指针指向的值不能修改
  2. 指针常量 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就会报错

函数提高

  1. 函数默认参数

    c++中,函数的形参列表中的形参是可以有默认值的,调用函数时没有传入参数就用默认值,如果传入了参数就用传入的参数

    int func(int a, int b=20, int c=30){
        return a+b+c;
    }
    

    注意事项

    • 如果某个位置已经有了默认参数,那么从这个位置往后,从左到右都必须有默认值。
    • 如果函数声明有默认参数,函数实现就不能有默认参数。(声明和实现只能有一个有默认参数,避免二义性)
  2. 函数占位参数

    c++函数的形参列表里可以有占位参数

    void func(int a, int ){//第二个参数就是占位参数,在调用的时候,也要传入两个参数。
        cout<<a<<endl;
    }
    void func(int a, int =10 ){} //占位参数还可以有默认参数
    
  3. 函数重载

    可以让函数名相同,提高复用性 重载就是参数不一样就行(包括类型,个数,顺序)

    函数的返回值不可以作为函数重载的条件(有二义性,不知道调用哪一个)

    1. 函数重载必须在同一个作用域下、
    2. 函数名称相同
    3. 函数参数类型不同,或者个数不同,或者顺序不同。

    引用作为重载条件

    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;//圆心    
};
构造函数和析构函数
  • 构造函数:主要作用在于创建对象的成员属性赋值,构造函数由编译器自动调用
  • 析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作, 将堆区开辟的数据进行释放草走

构造函数语法:类名(){}

  1. 没有返回值,也不写void
  2. 构造函数可以有参数,可以发生重载

析构函数语法:~类名(){}

  1. 没有返回值,也不写void

  2. 函数名称与类名相同,在名称前加上符号

  3. 析构函数不可以有参数,因此不可以发生重

    //拷贝构造函数
    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§;
  • 以值方式返回局部对象

构造函数调用规则

  1. 默认构造函数(无参,函数体为空)
  2. 默认析构函数(无参,函数体为空)
  3. 默认拷贝构造函数,对属性进行值拷贝

构造函数调用规则如下:

  • 如果用户定义有参构造函数,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个函数

  1. 默认构造函数(无参,函数体为空)
  2. 默认析构函数(无参,函数体为空)
  3. 默认拷贝构造函数,对属性进行值拷贝
  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 子类 : 继承方式 父类

继承方式一共三种

  1. 公共继承
  2. 保护继承
  3. 私有继承

父类中的私有属性不管以哪一种方式继承,子类都不能访问

公共继承:父类中的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关键字,让函数变成虚函数
  • 动态多态的函数地址晚绑定 ----- 运行阶段确定函数地址

动态多态满足条件

  1. 有继承关系
  2. 子类重写父类的虚函数

动态多态使用条件

  • 父类的指针或者引用, 指向子类对象

重写:函数返回值类型 函数名 参数列表 完全一致称为重写

多态优点

  1. 组织结构清晰

  2. 可读性强

  3. 对于前期和后期扩展以及维护性高

纯虚函数和抽象类

纯虚函数语法: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; 类名::~类名(){}

总结

  1. 虚析构或纯虚析构就是用来解决通过父类指针释放子类对象
  2. 如果子类中没有堆区数据,可以不写为虚析构或纯虚析构
  3. 拥有纯虚析构的类也属于抽象类

文件操作

通过文件可以将数据持久化

c++中对文件操作需要包含头文件

  1. 文本文件 ASCLL码存
  2. 、二进制文件 二进制存储

操作文件的三大类

  1. ofstream:写操作
  2. ifstream:读操作
  3. fstream:读写操作

写文件步骤

  1. 包含头文件 #include
  2. 创建流对象 ofstream ofs; 写文件是ifstream ifs;
  3. 打开文件 ofs.open(“文件路径”,打开方式)
  4. 写数据 ofs<<“写入的数据”
  5. 关闭文件 ofs.close();

文件打开方式

  1. ios::in 为读文件而打开文件
  2. ios::out 为写文件而打开文件
  3. ios::ate 初试位置:文件尾
  4. ios::app 追加方式写文件
  5. ios::trunc 如果文件存在先删除,再创建
  6. 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
  • 使用函数模板有两种方式:自动类型推导、显示指定类型
  • 模板的目的是为了提高复用性,将类型参数化

普通函数与函数模板区别:

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

普通函数与函数模板的调用规则

调用规则如下:

  1. 如果函数模板和普通函数都可以实现,优先调用普通函数
  2. 可以通过空模板参数列表来强制调用函数模板
  3. 函数模板也可以发生重载
  4. 如果函数模板可以产生更好的匹配,优先调用函数模板

模板也有局限性,有些特定数据类型,需要具体化方式做特殊实现

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;
}

类摸吧和函数模板的区别

  1. 类模板没有自动类型推导的使用方式
  2. 类模板在模板参数列表中可以有默认参数
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类型

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

类模板中成员函数和普通类中成员函数创建时机是有区别的:

  • 普通类中的成员函数一开始就可以创建
  • 类模板中的成员函数在调用时才创建

类模板对象做函数参数

  1. 指定传入的类型 直接显示对象的数据类型 常用
  2. 参数模板化 将对象中的参数变为模板进行传递
  3. 整个类模板化 将这个对象类型模板化进行传递
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;
}

类模板分文件编写

类模板中成员函数创建时机是在调用阶段,导致分文件编写时连接不到

解决方案

  1. 直接包含.cpp源文件
  2. 将声明和实现写到同一个文件中,并更改后缀名为.hpp, hpp是约定的名称,并不是强制

STL

目的就是提升代码复用性

  • STL( standard template livrary)标准模板库
  • STL从广义上分为,容器、算法、迭代器
  • 容器和算法之间通过迭代器进行无缝连接
  • STL几乎所有的代码都采用了模板类或模板函数

STL六大组件、

容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器

  1. 容器:各种数据结构,如vectro、list、deque、set、map等
  2. 算法:各种常用的算法,如sort、find、cop、for_each等
  3. 迭代器:扮演了容器与算法的胶合剂
  4. 仿函数:行为类似函数,可作为算法的某种策略
  5. 适配器:一种用来修饰容器或者仿函数或迭代器接口的东西
  6. 空间适配器:负责空间的配置与管理
#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;
}
排序
拷贝和替换
算术生成
集合
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++ Are You Ready To Learn C++? Welcome and have fun with C++! Today only, get this Book for just $7.99. Regularly priced at $12.99. Read on your PC, Mac, smart phone, tablet or Kindle device. Do you want to learn C++? In that case, you've come to the right place! Learning a programming language is not an easy work if you don't have the RIGHT system. It requires time, money and desire. You must search an academy or a teacher, achieve coordination with them, or worse, adapt your own time to their class times. You also have to pay the high fees, month to month, and what is even more annoying is this: you will probably have to go to a special place in order to practice the new programming language! You see, when it comes to learning a new programming language we are ALL in the same game, and yet most poeple don't realize it. I made this crash course for a reason… I made this course to give YOU a solution. This crash course about C++ programming is not only going to teach you the basics of C++ in a didactic way, furthermore, you will learn C++ language WHEN you want, and more important, WHERE you want (It could even be at your home!) I made this crash course to show you HOW you can learn C++ FASTER than you ever thought possible. I will teach YOU step by step the C++ Language extremely quickly. I will TAKE you through a step by step guide where you simply can't get lost! This course-book will allow you to practice, learn and deepen your knowledge of C++ as a new programming language in an entertaining, interactive, autonomous and flexible course. End-of-Chapter Exercises "Tell me and i'll forget. Show me and i may remember. Involve me and i learn". Because we know that: each C++ chapter comes with an end-of-chapter exercise where you get to practice the different C++ properties covered in the chapter. If you are determined to learn no one can stop you. Stop procrastinating and start NOW! Learning C++ is something that is a really worth investing time. The C++ course is now available in Amazon and it is just for $7.99. This is a no-brainer! Crash it! Here Is A Preview Of What You'll Learn When You Download You Copy Today: What is C++ Used For? Why Use C++? What You Need to Code in C++ Program Structure and Comments Functions and Return Values Variables and Data Types Characters and Strings Boolean Values Constants Operators Basic Arithmetic Advanced Data Types Loops and Conditional Statements Arrays and Pointers Functions Using Parameters with a Function Definition Operator Overloading Object Oriented Programming with C++ Inheritance Much, much more! Table of Contents Chapter 1: An Introduction Chapter 2: C++ Basics Chapter 3: Variables and Data Types Chapter 4: Operators Chapter 5: Advanced Data Types Chapter 6: Loops and Conditional Statements Chapter 7: Arrays and Pointers Chapter 8: Functions Chapter 9: Operator Overloading Chapter 10: Object Oriented Programming with C++ Chapter 11: Inheritance

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值