一篇文章带你了解C++基础

C++

数据类型

普通

整数
    short(2),int(4),long(4/win,8/linux*64),long long(8)
    short n = 32767;
浮点
    float(4/7位有效), double(8/15-16位有效)
    科学计数法 float a = 3e2 = 3*10^2 = 300
字符
    char c = 'a'; //单引号只能放一个字符(字母数字符号)占一个字节, (int)c 获取字符c的ascll码, 'a'->97 'A'->65
    char c[] = "abcd"  //定义多个字符的字符串, 必须双引号
    string s = "string";  //c++风格字符串,使用前必须引入 #include <string>, string就是char的数组
    转义
        \n(换行), \t(制表符)一个指标占8个位置, 
布尔
    bool b = true;  //占一个字节
数组
    数据放在连续的内存空间中,数据类型相同,数组名是个常量不能赋值
    int arr[5];  //定义整数类型容量为5的空数组
    arr[0]= 1;  //数组0的位置设置为1
    int arr[5] = {1,2,3,4,5}; //定义数组并直接赋值, 数量不足的就会填写0, 不指定容量就创建大小一样的
    int arr[5] {1,2,3,4,5}; //定义数组并直接赋值, 数量不足的就会填写0, 不指定容量就创建大小一样的
    int arr[] = {1,2,3};  //自适应长度
    int arr[][] = {{1,2,3},{4,5,6},{7,8,9}}  # 二维数组
    int arr[3][3] = {1,2,3,4,5,6,7,8,9}  # 二维数组,自动分割,必须定义容量
    int arr[][3] = {1,2,3,4,5,6,7,8,9}  # 二维数组,自动分割,每3个取为一列
    cout<<arr<<endl;  //输出数组首地址
    cout<<&arr[-1]<<endl; //输出元素的地址,第0个就是首地址

string

string s = "string";  //c++风格字符串,使用前必须引入 #include <string>, string就是char的数组

构造

string s = "s";  //直接创建
string n;  //空字符串
const char* c = "char";  //c字符串
string s_char(c);  //用c字符串创建
string s_copy(s_char);  //拷贝
string s_mul(10,'a');  //重复->10个a

赋值

string s1 = "ss";
string s2 = s1;
string s3 = 'a';
string s4;
s4.assign("ss");
s4.assign("123456",2);  //赋值前两个
s4.assign(s3);
s4.addign(10,'a')  //重复10个a

拼接

string s = "a";
s+="b";  // s->ab
s.append("c");  //s->abc
char* c = "d";
s.append(c);  //s->abcd
s.append("ef", 1);  //s->abcde , 截取1个
s.append("asd11ddd", 2,3);s->abcded11, 从索引为2截取3个

查找

string s = "abcde";
int index = s.find("de");  //从左向右查找de的位置,返回第一个查找到起始字母的位置, 不存在返回-1
int index = s.rfind("de");  //从右侧向左查找de的位置,返回第一个查找到起始字母的位置, 不存在返回-1

替换

string s = "abcdef";
s.replace(1,3,"11");  //从索引为1开始的3个字符替换为11

比较

string s1 = "acc";
string s2 = "abc";
int r = s1.compare(s2);  //0->s1=s2, 1->s1>s2, -1-> s1<s2  

字符存取

string s = "hello";
s[1]->e
s.at(1) ->e
s[1] = 's';  //hsllo
s.at(1) = 's'; 

插入删除

string s = "abcd";
s.insert(1, "11"); //a11bcd
s.erase(1,2) ; //abcd, 从索引1删除2个

截取

string s = "abcde";
string sub = s.substr(1,3); //bcd, 索引1开始截取3个

//截取邮箱名字
string email = "xingshuyin@outlook.com";
int end = email.find("@");
string name = email.substr(0, end);  //xingshuyin ,, 左闭右开

类型转换

//字符串转数字
string s="123";
int s_int = std::atoi(s.c_str()); //获取id, 通过string的c_str()强制转换为int类型

//数字转字符串
string num = to_string(5);


分割(不含中文)

string line;
while (getline(rooms_file, line)) {  //读取文件的每行为string
  vector<string> values;
  int size = line.size();
  int start = 0;
  int end = 0;
  int index = 0;
  for (; index < size; index++) {
    if (line[index] == ',') {  //分隔符为逗号(,)
      start = end;
      end = index;
      if (start == 0) {
        values.push_back(line.substr(start, end - start));
        continue;
      }
      values.push_back(line.substr(start + 1, end - start - 1));
    }
  }
  start = end;
  end = index;
  values.push_back(line.substr(start + 1, end - start - 1));  //最后一个值没逗号需要最后额外截取
  vector<string>::iterator begin = values.begin();
  int id = (int)(*(begin + 1)).c_str(); //获取id, 通过string的c_str()强制转换为int类型
  Room r = Room(*(begin), id, (int)(*(begin + 2)).c_str(), *(begin + 3));
  this->rooms.insert(make_pair(id, r));
  //for (vector<string>::iterator i=values.begin(); i != values.end(); i++) {
  //  cout << *i << "\n";
  //}
}

指针

指针的类型决定指针+1后内存移动的距离,即地址的变化值

int a=1;
int* p;  //创建指针变量,占用四个字节(32位系统)八个字节(64位系统)
p = &a;  //把p赋值a的地址, &a->获取a地址
cout<<*p<<ends;  // *p -> 解引用p的地址,就是代表指向变量的值,可以获取和修改, *p = a

空指针(指针初始化,不能访问)
    int* p = NULL;  //指向地址中的0地址(0-255的内存是系统的,不能访问即使用 *p)
野指针
    int* p = (int*)0x1222;  // 指向未申请的内存空间
常量指针
    const int* p = &a;  //可以修改指向, 但不能用 *p 修改内存的值(const 后边是int*)
指针常量
    int* const p = &a;  //指向不可以改(const 后边是指针p), 但可以用 *p 修改内存的值
常量指针+指针常量
    const int* const p = &a;  //指向和值都不可以改

指向数组
    int arr[] = {1,2,3};
    int* p = arr;  //指向数组不用添加&,因为arr就代表首地址, 则此时 *p = arr[0]
    p++;  //因为p是int类型, p++后就会向后偏移四个字节,然后p就指向arr的下一个元素,即 *p=arr[1]

结构体指针
    People* p;  //创建结构体指针
    p = &xing;  //指针指向结构体地址
    p->name = "张";  //通过指针访问结构体(class)属性, struct的struct访问属性不用->而是.

引用

相当于起别名

int a = 1;
int& b = a;  //a=1,b=1
b = 2;  //a=2,b=2

结构体

struct People{  //默认是公共权限
    string name;
    int age;
    int score[5];
    struct hobby hob[2];  //嵌套结构体
}xing ;  //在末尾顺便创建一个变量,可以不写, 结尾有分号
struct People xing;
xing.name="xing";
xing.age = 22;
xing.score = {1,2};

struct People xing = {"xing",22,{1,2}} ;
People xing = {"xing",22,{1,2}} ;  //创建结构体变量时,struct可以省略

People students[] = {{"张",15,{1,2}},{"六",16,{2,3}}}  // 创建结构体数组

结构体指针
    People* p;  //创建结构体指针
    p = &xing;  //指针指向结构体地址
    p->name = "张";  //通过指针访问结构体属性, struct的struct访问属性不用->而是.

输出

输出内容 c out,单引号(一个字符,字母数字符号)和双引号(多个字符)不同, endl 换行结尾 ends空字符结尾

#include <iostream>
cout <<a << 'month' << c << d << endl; 

printf("啊");

输入

int a;
cin >> a;  //输入赋予a

运算

+ - * / % ++ -- += -= *= /= %= == != < > <= >= && ! ||
整数相除结果是结果的整数部分
小数取模必须有一个是整数

结构

if

if(a==1){ cout << a<<ends; } else if(a==2){ cout << a<<ends;} else{cout<<a<<ends;}
bool c = a>b?a:b;  //三目运算符, a>b
a>b?a:b = 10;  //可以返回变量并进行赋值

for

for(int i=0;i<10;i++){
    if(i==2){
        break;
    }
    else if(i==5){
        continue
    }
    cout<<i<<ends;
}

switch

switch(a){
    case 1:
        cout<<1<<ends;
        break;
    case 2:
        {  //代码过长时加上{}防止出错
            cout<<2<<ends;  //case中不能进行赋值操作,所以赋值操作要放到另外一个函数中做
            break;
        }

    default:
        cout<<3<ends;
        break;
}

while

while(a==1){
    cout<<a<<ends;
}


do{
    cout<<a<<ends
}while(a==1);

跳转

FLAG;  //跳转标签
goto FLAG: //跳转到标签后执行

函数

分文件编写

头文件

#pragma once  //防止头文集重复包含
#include<string>
#include<iostream>  //标准输入输出
#include<math.h>
using namespace std;  //标准命名空间, 主文件写了这也得写

class point{  //保留函数和变量的声明
    float x;
    float y;
    public:
        float distant(point p);
        float getX();
        float getY();
        void init(float x,float y);
};//必须有分号
class cycle{
    point center;
    float r;
    public:
        void init(point & p,float r);
        void getCenter();
        bool isInner(point p);
};


源文件

#include "cycle.h"  //引入头文件
void cycle::init(point& p,float r){  //用过cycle::区分这是那个class的函数
    this->center=p;
    this->r = r;
}
void cycle::getCenter(){
    cout<<"("<<center.getX()<<","<<center.getY()<<")"<<endl;
}

bool cycle::isInner(point p){
    if( this->center.distant(p)<this->r ){
        return 1;
    }return 0;
}

//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
float point::distant(point p){
    return sqrt((x-p.x)*(x-p.x)+(y-p.y)*(y-p.y)) ;
}
void point::init(float x,float y){
    this->x=x;  //属性和参数一样时要用this->来使用属性
    this->y=y;
}
float point::getX(){
    return x;
}
float point::getY(){
    return y;
}
int main(){
    cycle c1;
    point p;
    point p2;
    p2.init(5,6);
    p.init(1,1);
    c1.init(p,7);
    c1.getCenter();
    cout<<c1.isInner(p2);
}

传值

int, float, double, char, string, struct, class
他们的引用,地址,值转递都基本是一样的


引用传值

引用传值本质: 传值时传的为类似 int* const a常量指针, 使用时使用的是*a, 只不过是由编译器自动实现

想要防止修改引用的变量,可以把形参改为 const int& a

void fun(int& a){  //传值方式为引用
  a = 2;  //函数内修改也会影响传值的变量
}
int main(){
  int a=1;
  fun(a);  //和值传递一样,传入变量本就行
}

struct(class类似)引用传值
S& fun(S& s){
    s.age=11;  //引用的值直接用点(.)访问或修改属性
    return s;
}
int main(){
    S s1={"jack",1};
    S& s2 = fun(s1);  //返回的引用类型要用引用类型接收
    cout<<s1.age<<"--"<<s2.age<<endl;  //s1.age=2,s2.age=2
    s2.age=3;
    cout<<s1.age<<"--"<<s2.age;  //s1.age=3,s2.age=3
    return 0;
}

数组的引用传值
数组名是指针,傻了吧!!!

指针传递

viod fun(int* a){
  *a = 2;  //函数内修改也会影响传值的变量
}
int main(){
  int a = 1;
  fun(&a);  //需要传入指针
}

数组的指针传递

数组形参有两种形式 int* arr 和 int arr[]

int* fun(int* arr){
    arr[0]=2;
    return arr;
}
int main(){
  int arr1[2]={1,2};
  int* arr2 = fun(arr1);  //数组的地址传值不需要加*,但接收的引用值需要
  cout<<arr1[0]<<"--"<<arr2[0];  //arr1[0]=2,arr2[0]=2
  arr2[0]=3;
  cout<<arr1[0]<<"--"<<arr2[0];  //arr1[0]=3,arr2[0]=3
}

int* funArr(int arr[]){
    arr[0]=2;
    return arr;
}
int main(){
  int arr1[2]={1,2};
  int* arr2 = fun(arr1);  //数组的地址传值不需要加*,但接收的引用值需要
  cout<<arr1[0]<<"--"<<arr2[0];  //arr1[0]=2,arr2[0]=2
  arr2[0]=3;
  cout<<arr1[0]<<"--"<<arr2[0];  //arr1[0]=3,arr2[0]=3
}

结构体(class类似)的指针传递
S* fun(S* s){
    s->age=2;
    return s;
}
int main(){
    S s1={"jack",1};  //结构体地址传值,与引用返回
    S* s2 = fun(&s1);  //传入结构体地址,返回指针,
    cout<<s1.age<<"--"<<s2->age<<(*s2).age<<endl;  //指针访问结构体属性要用->,也可以解引用指针,就是需要加括号
    s2->age=3;
    cout<<s1.age<<"--"<<s2->age;
}

值传递

void fun(int a){  //形参会重新申请内存储存数据
  a = 6;  //函数内修改不会影响传值的变量
}

返回值

引用类型

一般返回引用类型时需要传入引用类型才有用

int& fun(int& a){  //返回引用类型, 
  a=2;
  return a;
}
int main(){
  int a=1;
  int& b=fun(a); //b=2,a=2, 用引用类型接收引用
  b = 3; //b=3,a=3
}

地址类型

其实就是引用的还原版

不要返回局部变量的地址,因为函数结束后就会被销毁返回的数据会保留一次,即只能访问一次,再次访问机会变化

int* fun(int* a){
    *a = 4;
    return a;
}
int main(){
  int a = 1;
  int* b = fun(&a);
  cout<<a<<"--"<<*b<<endl; 
  *b = 3;
  cout<<a<<"--"<<*b<<endl;
}

返回数组
int* funArr(int arr[]){
    arr[0]=2;  //数组名本身就是指针,使用时不用加*
    return arr;
}
int main(){
  int arr1[2]={1,2};
  int* arr2 = fun(arr1);  //数组的地址传值不需要加*,但接收的引用值需要
  cout<<arr1[0]<<"--"<<arr2[0];  //arr1[0]=2,arr2[0]=2
  arr2[0]=3;
  cout<<arr1[0]<<"--"<<arr2[0];  //arr1[0]=3,arr2[0]=3
}

返回值

int fun(int a){
  a = 2;
  return a;
}
int main(){
  int a = 1;
  int b = fun(a);
  cout<<a<<"--"<<b<<endl; //a=1,b=2
  b=3;
  cout<<a<<"--"<<b<<endl; //a=1,b=3
}

函数重载

相同作用域,函数名相同,参数,类型,个数,顺序不同, 不能通过函数返回类型区分

void fun(int a){} //fun(int a=10)
void fun(int& a){}   //fun(const int a=10)或fun(10)

void fun(int a, string b="ss"){}  //尽量不要默认参数,会和无默认的参数冲突
void fun(int a, string b){}

系统

system("pause");  //代码暂停执行
system("cls");  //清屏
sizeof(a)  //获得变量或数据类型所占空间

内存分区

内存分区(不同分区放在不同内存位置)


//程序运行前>>>>>>>>>>>>>>>>>
代码区(系统管理,存放函数二进制代码,共享,只读)
全局区(存放全局变量(不在函数体中),静态变量(static int a;),常量(const int a;即字符串常量->"word"),,程序结束后系统控制)
    const int a;  //函数体外,全局常量
    int b;  //函数体外,全局变量
    static int c;  //静态变量
    string d = "str";  //字符串常量
// 程序运行后>>>>>>>>>>>>>>
栈区(编译器自动分配,存放函数参数值,局部变量)
    不要返回局部变量的地址 return &a;  函数结束后内存就会自动释放, 返回的数据会保留一次,即只能访问一次,再次访问机会变化
    const int b;  //函数体内,局部常量
    指针也是局部变量
堆区(程序员分配和释放)
    int* p = new int(10);  //创建存放在堆区的变量,并返回对应地址,这样就能返回
    int* fun(){
        int* p=new int(10);  //堆区创建数值
        int* arr = new int[10];  //堆区创建数组
        delete[] arr;  //堆区删除数组
        return p;
    }
    int main(){
        int* p = fun();
        delete p;  //删除堆区的变量
    }

类和对象

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

class Circle{  //空对象占用一个字节内存
public:  //公共权限,类内和类外都可以访问 {1}设置默认值,可以空
    int r{} ;  //非静态成员变量占类对象的空间
    static float PI;  //静态成员变量占用全局区空间
    static void chagePI(float p){
      PI=p;
    }
    double circleLong(){
        return 2*PI*r;
    }
    string getName(){
        return name;
    }
    string setName(string name){
        this->name = name;  //为防止变量冲突,可以用this->访问class的属性
        return name;
    }
    Circle(float r):r(r){}
protected: //类内和子类可以访问

private:  //只类内可以访问(默认)
    string name="Jack";

};  //结尾有分号
float Circle::PI=3.14;
int main(){
    Circle c;  //创建实例
    c.r=10;
    double circleLong = c.circleLong();
}

构造/析构函数

如果写了有参就不会默认提供无参,写了拷贝就不会默认提供无参和有参

构造函数
    无返回值(不用写void),与类名相同,可以有参数,可重载,只执行一次
析构函数
    无返回值,无参数,与类名相同,无参数
class cls{
  string name;
  int* age;
  public:
    void setAge(int a){
      age = new int(18);
    }
    cls(){  //默认构造函数,对象创建后调用一次

    }
    cls(string name, int age):this->name(name),this->age(age){  //有参构造函数,this->name(name)初始化列表

    }
    cls(const cls &c){  //拷贝构造函数(默认创建),复制实例, 值传递, 返回值时会调用
        name=c.name;  //私有属性也可以拷贝, 默认的时所有属性都拷贝
        age = c.age;  //浅拷贝,堆区数据变成了两个的公共数据, 其中一个销毁后另外一个就无法访问了
        age = new int((*c).age)  //深拷贝
    }
    ~cls(){  //析构函数,对象销毁前调用一次,释放堆区的数据
    //堆区的数据复制的是地址
    delete age;
    }
};
int main(){
  cls c1;  //默认构造函数创建实例, 不要加括号, 不然会被识别为函数的声明
  cls c2 = cls("Jack");  //有参构造函数创建实例
  cls c2("Jack");  //括号法有参构造(好用)
  cls c2 = string("Jack");  //隐式有参构造
  cls c3 = cls(c2);  //拷贝构造函数创建实例, 复制c2的参数
  cls c3(c2);  //括号法拷贝构造
  cls c3 = c2;  //隐式拷贝构造
}

类作为属性

class cls1{
  public:
  string name;
    cls(string name,int age):name(name),age(age){  //有参构造函数
};

class cls2{
public:
    string name;
    cls1& c1;  //引用传class为成员属性
    cls2(string name, cls1& c1):name(name),c1(c1){}
};

int main(){
  cls1 c1("Jack");
  cls2 c2("Mike",c1);  //构造函数创建实例
  c1.name="Jack+";  //c2.c1.name=Jack+
}

静态成员属性

静态成员变量

所有对象共享数据, 编译阶段分配内存,类内声明,类外初始化

class cls{
  public:
    static int count;  //类内声明
    static void clear(){  //静态成员函数
      count=0;
    }
};
int cls::count=0;  //类外初始化,编译阶段分配内存
int main(){
  cls cls1;
  cout<<cls1.count; //公共属性才能直接访问,私有也只能类内访问
  cout<<cls::count; //公共属性才能直接访问
}

静态成员函数

共享, 函数里只能访问静态成员变量

class cls{
  public:
    static int count;  //类内声明
    static void clear(){  //静态成员函数,不能使用this,可以类内声明也可以类外
      count=0;
    }
};
int main(){
  cls c1;
  c1.clear();  //公共属性才能直接访问
  cls::clear();
}

this指针

this指向调用的对象,是一个指针常量不能修改指向

形参与成员变量冲突,返回对象本身

class cls{
public:
  int a;
  cls& add(int& a){
    this->a+=a;  //与参数冲突
    return *this;  //链式编程原理,必须是返回引用,不然只会修改一次,以后的修改都是修改产生的副本
  }
};

空指针

class cls{
  public:
  string name;
  void show(){
    cout<<"show"<<endl;
  }
  void showName(){
    if(this==NULL){return;}  //解决空指针报错
    cout<<name<<endl;
  }
};
cls* c=NUll;
c.show();  //正常
c.showname();  //报错,需要访问成员变量时报错,this指向空

常函数

class cls{
  public:
  int a;
  mutable int b;
  void change() const{  //常函数
    a++;  //不能修改指针指向的值
    b++;  //可以修改mutable 修饰的变量
  }
};

常对象

class cls{
  public:
  int a;
  mutable int b;
  void change() const{  //常函数,const修饰this
    a++;  //不能修改指针指向的值
    b++;  //可以修改mutable 修饰的变量
  }
};
int main(){
  const cls c;  //常对象,只能调用常函数,不能修改属性
}

友元

使类外面的可以访问类内的所有属性,除父类的私有属性

子类的友元无法对父类生效

全局函数友元

class cls{
  int age;
  friend void getage(cls c);  //添加友元函数
  public:
  string name;
  cls(int age,string name):age(age),name(name){}
};
void getage(cls c){
  cout<<c.name<<c.age;
}
int mian(){
  cls c("Jack",19);
  getage(c);
}

类友元

class cls{
  int age;
  friend void getage(cls c);  //添加友元函数
  friend class cls2;  //添加类友元
  friend void cls2::getage(cls c)  //添加成员函数友元
  public:
  string name;
  cls(int age,string name):age(age),name(name){}
};
class cls2{
  public:
  void getage(cls c){
    cout<< c.age;
  }
}

重载运算符

      • / <<(通常不会用成员函数重载) >>
#include <iostream>
#include <string> //c++风格字符串
using namespace std;

class cls{
public:
    int num;
    int* self_num;  //声明指针
    cls(int num,int self_num){
        this->num=num;
        this->self_num=new int(self_num);  //创建数值的指针
    }  //构造函数
    cls& operator++(){  //重载前置++ -> ++a
        this->num++;
        return *this;
    }
    int operator++(int){  //重载后置++ -> a++
        int temp;
        temp = this->num;
        this->num++;
        return temp;
    }
    bool operator==(cls const& c){  //重载关系运算符
        if(this->num == c.num){
            return true;
        }
        return false;
    }
    void operator()(){  //仿函数,重载(),非常灵活,可以实现各种功能
        cout<<"rewrite()"<<endl;
    }
    cls& operator=(cls const& c){  //重载赋值运算符
        // this->num=c.num; //编译器默认
        if (this->self_num != NULL){  //存放在栈区的需要手动释放
            delete self_num;  //释放指针所指内存
        }
        this->num = c.num;
        this->self_num=new int(*c.self_num);  //避免内存重复释放
        return *this;
    }
    ~cls(){
        if (this->self_num != NULL){  //存放在栈区的需要手动释放
            delete self_num;  //释放指针所指内存
        }
    }
};
ostream& operator<<(ostream& out, cls& c){  //重载输出运算符
    out<<"num:"<<c.num<<" self_num:"<<*c.self_num<<endl;
    return out;
}


int main(){
    cls c1(15,16); //首先要写构造函数
    cls c2(15,16); //首先要写构造函数
    cls c3(15,16); //首先要写构造函数
    cls c4(15,16); //首先要写构造函数
    cout<<c1++<<endl;
    cout<<c1++<<endl;
    c4=c3=c1;
    cout<<c4;
    if(c1==c2){
        cout<<"ture"<<endl;
    } else{
        cout<<"false"<<endl;
    }
    c1(); //仿函数, 可以不创建实例(创建匿名对象)
    return 0;
}

继承

构造析构顺序-> 父(构造),子(构造),子(析构),父(析构)
子类: 继承方式(public,protected,private) 父类 , 继承方式就是继承的父类的属性访问的最大权限, 若是 protected则public变成protected,private不变
virtual虚继承, 修饰的类为虚基类, 菱形继承避免重复继承

单继承-多继承

class Base{  //定义父类(基类)
public:
    string name{"搜索"};
    int age{12};  //{}声明默认值, 可以为空
    static string num;
    Base()=default;
    ~Base()= default;
};
class Base2{  //定义父类(基类)
public:
    string name{"搜索"};
    Base2()=default;
    ~Base2()= default;
};
string Base::num="base-num";  //Base作用域下的静态num初始化

//  构造析构顺序->  父(构造),子(构造),子(析构),父(析构)
//  子类: 继承方式(public,protected,private) 父类  , 继承方式就是继承的父类的属性访问的最大权限, 若是 protected则public变成protected,private不变
//  virtual虚继承, 修饰的类为虚基类, 菱形继承避免重复继承

class People:virtual public Base, public Base2{ //继承Base, 多继承,分割, 不建议多继承
public:
    string home;
    int age{};  //和父类重名, 访问父类的属性或方法需要添加作用域  People.Base::age, 访问子类的 People.age
    static string num;
    People()=default;
    People(string name, int age, int base_age, string home){
        this->Base::name=name;  // 不重名的属性直接使用
        this->Base2::name=std::move(name);  // 不重名的属性直接使用
        this->age=age;
        this->Base::age=base_age;  //父类相同属性赋值
        this->home=std::move(home);
    }
    ~People()= default;
};
string People::num="people-num";  //People作用域下的静态num初始化

void inherit(){
    People p1("Jack",1,2,"beijing");
    cout<<p1.Base::name<<endl;
    cout<<p1.Base::num<<endl;  //用子类对象访问父类静态属性,添加作用域 ->Base::
    cout<<p1.num<<endl;  //用子类对象访问子类静态属性
    cout<<Base::num<<endl;  //用类名访问静态属性
    cout<<People::num<<endl;  //用类名访问静态属性
}

菱形继承

class animal{
public:
    int age{0};
    string name;
    virtual void move(){  
    //虚函数, vfptr,相当于是创建一个函数的指针, 子类继承是继承的是指针, 所以即使继承两次也只有一个函数
        cout<<"animal move"<<endl;
    }
};

class sheep:virtual public animal{
public:
    string sheep;
};
class tuo:virtual public animal{
public:
    string tuo;
};
class sheep_tuo:public sheep,public tuo{  
//如果父类不使用virtual继承animal ,就会出现多个animal的属性,分别隶属于sheep和tuo,需要用::sleep访问, 加上virtual后就可以直接访问
public:
    sheep_tuo()=default;
    sheep_tuo(const string& name, int age, const string& sheep, const string& tuo){
        this->name=name;
        this->age=age; //必须父类使用了virtual才能这样用, 不然就要加作用域
        this->sheep=sheep;
        this->tuo=tuo;
    }
};
void inherit(){  //继承测试
    sheep_tuo st("羊驼",15,"山羊","骆驼");  //菱形继承
    st.move();  //直接使用virtual修饰的函数
    cout<<st.age<<endl; --15
}

动态多态

class base{
public:
    virtual void move(){
        //加上virtual后, 子类重写这方法, 函数地址晚绑定,
        // vfptr-虚函数(表)指针, 指向一个对象自己的虚函数表, 表内记录虚函数地址作用域为父类,
        cout<<"base move";
    }
};
class cls1:public base{
public:
    virtual void move() override{  //virtual 可以不加
        //子类重写虚函数, 会覆盖自己的虚函数表里的地址, 把作用域改为子类自己
        cout<<"cls1 move";
    }
};
class cls2:public base{
public:
    void move() override{
        cout<<"cls2 move";
    }
};

void duo_tai(cls_base& cls_base){  //动态多态, 形参为父类指针(或引用), 传入子类对象
    //子类重写父类虚函数, 在使用时传入子类,就会触发子类(cls1,cls2)重写的方法,而不是父类的(cls_base)
    cls_base.move();
}

int main(){
    cls1 c11;
    cls2 c21;
    duo_tai(c21);  //动态多态 输出--cls2 move
    return 0;
}

多态计算器

class abstract_calculator{ //定义计算器基类
public:
    int num1{};  //定义需要计算的两个数
    int num2{};
    virtual int result()=0; //定义纯虚函数, 类变成抽象类,不能实例化,子类要不重写纯虚函数就无法实例化
};
class add_calculate:public abstract_calculator{  //继承基类,计算方式
public:
    int result() override{  //重写求解函数
        return num1+num2;
    }
};
class minus_calculate:public abstract_calculator{
    int result() override{
        return num1-num2;
    }
};
class multiple_calculate:public abstract_calculator{
    int result() override{
        return num1*num2;
    }
};
class division_calculate:public abstract_calculator{
    int result() override{
        return num1/num2;
    }
};
void calculate(){
    abstract_calculator *c = new add_calculate;//基类指针,赋值子类对象,相同指针创建不同对象
    add_calculate a;
    a.num1=5;
    a.num2=6;
    c->num1=2;//初始化数值
    c->num2=3;
    cout<<a.result()<<endl;//获取结果
    cout<<c->result()<<endl;//获取结果
}

制作饮品

多态,纯虚函数,基类

class abstract_drinking{
public:
    virtual void boil()=0;  //纯虚函数
    virtual void brew()=0;
    virtual void pour()=0;
    virtual void put()=0;
};
class coffee:public abstract_drinking{
public:
    void boil() override{  //重写纯虚函数
        cout<<"coffee boil"<<endl;
    }
    void brew() override{
        cout<<"coffee pour"<<endl;
    }
    void pour() override{
        cout<<"coffee pour"<<endl;
    }
    void put() override{
        cout<<"coffee put"<<endl;
    }
};

void make(abstract_drinking& d){
    d.boil();
    d.brew();
    d.pour();
    d.put();
}
int main(){
    coffee c;
    make(c);  //传入子类
    return 0;
}

虚析构-纯虚析构

用基类指针,析构时调用子类析构函数

用于父类指针释放子类对象,子类没有堆区数据可以不写

class animal{
  string name{};
  void speak(){
    cout<<"animal speak"<<endl;
  }
  animal()=default;
  virtual ~animal{}=0;  // 
};
animal::~animal(){  //类外实现基类的析构
  cout<<"animal 析构"<<endl;
}
class cat:public animal{
public:
  string* color{};  //子类有创建在堆区的属性
  void speak() override{
    cout<<"cat speak"<<endl;
  }
  cat(string name, string color){
    this->name=name;
    this->color=new string(color);
  }
  ~cat override{
    delete color;
    cout<<"cat 析构"<<endl;
  }
};
void speak(animal a){
  a.speak();
}
int main(){
  cat c("小白","白");
  speak(c);
}

文件读写

普通文字

ios::out创建,覆盖写, ios::app创建,追加写, ios::ate打开文件末尾(不存在出错), ios::in读取(不存在出错),

ios::trunc清空数据, ios::binary二进制打开

ios::in|ios::out(fstream)读写,不创建, ios::in|ios::out(ofstream)写入,不创建

#include "fstream"
void file(){
    ofstream ofs;  //创建输出流对象
    //ofstream ofs("ofs.txt", ios::out|ios::app);  //直接读取文件
    ifstream ifs;  //创建输入流对象
    
    //写入--------------------
    ofs.open("ofs.txt", ios::out|ios::app);  //打开文件
    if(!ofs.is_open()){  //判断是否打开文件
        cout<<"读取失败"<<"\n";  //打开失败输出错误
        return;
    }
    ofs<<"ofs"<<"\n";  //普通文字写入文件
    
    //读取--------------------
    ifs.open("ifs.txt", ios::in);
    if(!ifs.is_open()){
        cout<<"读取失败"<<"\n";
        return;
    }
    
//    char a[1024]{0};  //创建char接受文字输入, 空间为1024个字节, 默认值为空
    string a;  /创建string接受文字输入
    ifs>>a;  // 普通文字读取, 读到空格结束
    cout<<"a: "<<a<<endl;
    ofs.close();
    ifs.close();
}

读取

#include "fstream"
ifstream ifs;  //创建输入流对象
ifs.open("ifs.txt", ios::in);
if(!ifs.is_open()){  //判断是否打开
    cout<<"读取失败"<<"\n";
    return;
}

string in;  //创建string接受文字输入
ifs>>in;  // 普通文字读取, 读到空格结束


写入

ofstream ofs;  //创建输出流对象
ofs.open("ofs.txt", ios::out|ios::app);  //打开文件
//ofstream ofs("ofs.txt", ios::out|ios::app);  //直接读取文件

if(!ofs.is_open()){  //判断是否打开文件
    cout<<"读取失败"<<"\n";  //打开失败输出错误
    return;
}
ofs<<"ofs"<<"\n";  //普通文字写入文件


二进制文件

#include "fstream"
void bfile(){
    ofstream bofs("bofs.jpg", ios::out|ios::binary);  //创建输出流对象
    if (!bofs.is_open()){
        cout<<"bofs读取失败"<<"\n";
        return;
    }
    ifstream bifs("bifs.jpg", ios::in|ios::binary);  //创建输入流对象
    if (!bifs.is_open()){
        cout<<"bifs读取失败"<<"\n";
        return;
    }
    bifs.seekg(0,std::ifstream::end);
    int length = bifs.tellg();  //获取图片长度
    cout<<length<<"\n";
    bifs.seekg(0, bifs.beg);

    char *bifs_a = new char [length];  // 创建用于接受的变量
    bifs.read(bifs_a,length);  // 读取文件
    bofs.write(bifs_a,length);  // 写入文件
}

逐行读取

std::ifstream all_worker("all_worker.txt", std::ios::in);  //打开文件
char tmp[50];  # 创建用于盛放每行数据的char数组, 容量大于行的最大字符数量
all_worker.seekg(0, all_worker.beg);  //确保从头开始读取, 把指针移动到开头
int count = 0;
while (!all_worker.eof()) {  //读到文件尾结束, 一定要是!不然就啥都读不出来了
    all_worker.getline(tmp, 50);  //读取行
    if (tmp[0] != NULL) {  //判断是否空行
        count++;
    }
}


string line;
while (getline(all_worker, line))
{
  if (!line.empty()) {
    cout << line << "\n";
  }
}


模板

模板参数→ 就相当于一种数据类型, 只不过是需要在传入数据后才会知道具体类型

创建

//template<class T>  -> 声明模板及模板参数T, <>里可以用逗号分隔以创建多个参数, 参数类型有typename和class
template<class T> void swapNum(T& a, T& b) {  //使用模板参数创建函数
  T temp = a;
  a = b;
  b = temp;
}
void fun_template() {
  int a = 2;
  int b = 3;
  swapNum(a, b); 
}

处理个例

template<class T>
void showList(T l[]) {
  for (int i = 0; i < sizeof(l); i++) {
    std::cout << l[i] << " ";
  }
}

template<> void showList(Persion p[]) {   // 针对某个特定数据类型使用具体化的模板函数
  for (int i = 0; i < sizeof(p); i++) {
    std::cout << p[i].name << " ";
  }
}


普通函数和模板函数的区别

  • 普通函数会有 隐式类型转换
  • 模板在自动类型推导(即不指定参数类型)时没有隐式类型转换, 指定模板类型就会隐式类型转换
  • 普通函数和模板重名
    优先使用普通函数(都可用的话)
    可以在调用时用空的<> 即 fun<>() 强制调用模板
    模板可以重载

类模板

template<class TYPENAME, class TYPELOCATION = int>  //声明多个未知变量, 可以有默认参数, 设置了默认参数在创建实例时可以不写这个参数
class Home{
public:
  TYPENAME name{};  //使用模板参数定义属性
  TYPELOCATION location{};

  void show() {
    std::cout << name << " " << location << "\n";
  }
};


//类模板做函数参数类型

void class_template(Home<std::string , int>& h) {  //必须指定类型
  h.name = "class_template";
  h.location = 5;
  h.show();
}

//类模板当作函数参数
template<class T>
void class_remplate_attr(T& h) {
  std::cout << h.name<<endl;
}

类模板继承

template<class T>
class base {
  T name;
};
template<class T_base, class T_sub>
class sub :public base<T_base> {  //继承模板类需要指定类型, 也可以用子类的模板参数代替
  T_sub  name_sub;
};

成员函数类外实现

template<class T>
class class_t1 {
  T name;
  class_t1(T n);
};
template<class T>  //声明为模板
class_t1<T>::class_t1(T n) { 类外的函数的作用域也需要传入模板参数类型
  this->name = n;
}

类模板分文件编写

  • 直接包含cpp, 一个类分为cpp和h正常是包含h, 但是模板需要包含cpp
  • 把声明和实现都放在hpp文件中, 然后包含hpp文件

类模板-全局函数

template<class T1, class T2>
class class_t2;

template<class T1, class T2>
void show2(class_t2<T1, T2> t2) {  //全局模板函数类外实现, 必须上面声明类下面定义类和声明友元-----------------------------类外实现
  std::cout << t2.name << " " << t2.age << "\n";
}

template<class T1, class T2>
class class_t2 {
  friend void show2<>(class_t2<T1, T2> t2);  //全局函数类外实现,声明友元
  friend void show1(class_t2<T1, T2> t2) {  //全局模板函数类内实现-----------------------------------------类内实现
    std::cout << t2.name << " " << t2.age << "\n";
  }
  T1 name;
  T2 age;
public:
  class_t2(T1 name, T2 age) {  //构造函数
    this->name = name;
    this->age = age;
  }
};


void class_friend() {
  class_t2<std::string, int> t("ss",5);
  
  show1(t);
}

通过引用数组长度

  template<class T, size_t N>
  List(T(&arr)[N]) {
    std::cout <<"size: "<< N << "\n";
  }

C++ STL

容器

vector

单端数组(尾部添加或删除),动态扩展

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wGrhAc5O-1646913520332)(image/src=http___service.ppkanshu.com_upload_images_20190829_c7a20743-ed07-4032-abd1-ca19c30cd730.jpg&refer=http___service.ppkanshu.jpg “”)]

头部插入慢, 访问速度快

构造-赋值-容量-大小

#include <vector>

vector<int> v1;
vector<int> v2(v1.begin(), v1.end());  //使用另一个数组的一部分创建
vector<int> v2(v1);//使用另一个数组创建
vector<int> v3(10,1);  //10个1

v.assign(10,1); //赋值10个1
vector<int> v2=v; //使用另一个数组赋值
vector<int> v3;
v3.assign(v.begin(), v.end());  //使用另一个数组的一部分赋值

bool empty = v.empty(); //是否空
int capacity = v.capacity();  //容量, 最大容纳数据量, 多余的位置会填充为0
int size = v.size();  //大小, 包含的数据数量
v.resize(10, 0);  //修改数组大小, 比原来小了会删除超出的部分, 比原来大了会填充0, capacity 不变
//自定义类型的vector.resize()需要添加对应类型的默认值-> p.resize(3,Person("a",1));

插入删除-存取

vector<int> v;
v.push_back(1);  //尾部添加
v.pop_back();  //尾部删除
v.insert(v.begin(), 1);  //在开头插入1, 开头插入需要移动所有数据效率低
v.insert(v.begin(), 2,1);  //在开头插入两个1
v.insert(v.begin(), v.begin()+1, v.end());  //插入另一个容器的部分区间->v.begin()+1, v.end()
v.erase(v.begin());  //删除开头的
v.erase(v.begin(), v.end());  //删除区间[begin, end)  左闭右开,  v.erase(v.begin(), v.end())->v.clear();
v.clear();  //删除全部

int num = v[0];  //获取第一个元素
int num = v.at(0);  //获取第一个元素
int front = v.front(); //获取第一个元素
int back = v.back();  //获取最后一元素

互换

vector<int> v;
for(int i=1;i<100000;i++){
  v.push_back(i);
}
v.resize(3); //v.capacity() =100000
vector<int>(v).swap(v);  //v.capacity() =3
//创建一个匿名的capacity为3的数组和v交换指针, 达到减小v容量的作用, 匿名的会在当行结束时销毁


预留空间

vector<int> v;
v.reserve(100000);  //直接预留100000个空间, 这样在后面第一次开辟内存时就会直接开辟100000, 而不需多次扩容


遍历

std::vector<int> l{1,2,3,4};  //创建向量
std::vector<int>::iterator begin = l.begin();  // 创建迭代器, 赋值为l的起始
std::vector<int>::iterator end = l.end();  // 创建迭代器, 赋值为l的末尾(指向最后一个元素的下一个)

// 遍历方法
for (std::vector<int>::iterator it = l.begin(); it != l.end(); it++) {  // 需要修改时用这个
  std::cout << *it << " ";  //it是没个数据指针
}
for each (int i in l)  //仅仅展示用这个
{
  std::cout << i << " ";
}
for_each(l.begin(), l.end(), show);  //#include <algorithm>, show需要在上面定义,最好用lambda的匿名函数

嵌套-嵌套遍历

#include "vector"
void vector_vector() {  //容器嵌套容器
  vector<vector<int>> con;
  vector<int> inner1 {1,1,1,1};
  vector<int> inner2 {2,2,2,2};
  vector<int> inner3 {3,3,3,3};
  vector<int> inner4 {4,4,4,4};
  con.push_back(inner1);
  con.push_back(inner2);
  con.push_back(inner3);
  con.push_back(inner4);
  
  //多重遍历, 用于修改
  for (std::vector<vector<int>>::iterator it = con.begin(); it != con.end(); it++) {
    for (std::vector<int>::iterator it2 = (*it).begin(); it2 != (*it).end(); it2++) {
      std::cout << *it2 << " ";
    }
    std::cout << "\n";
  }
  
  //用于查看
  for each (vector<int> i in con)
  {
    for each (int j in i)
    {
      std::cout << j << " ";
    }
    std::cout << "\n";
  }
}



排序

vector<int> v{1,2,5};
v.sort(compare<int>);  //降序

template<typename T>
bool compare(T v1, T v2){
  return v1>v2;
}

deque

两端插入删除快,访问数据慢,通过中控器维护数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LJKzyvIX-1646913520333)(image/src=http___images2018.cnblogs.com_blog_1389269_201808_1389269-20180803154953105-325812620.jpg&refer=http___images2018.cnblogs.jpg “”)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sDaAkZF8-1646913520334)(image/image.png “”)]

由中控器控制, 当端点满了时就会开辟一块新的空间连接到中控器的开头或结尾节点

构造-赋值-大小

#include <deque>

deque<int> d;
d.push_back(1);
deque<int> d {1,2,3};
deque<int> d2(d.begin(),d.end());  //使用另一个队列的一部分创建
deque<int> d2(d);  //使用另一个队列创建


d.assign(10,1); //赋值10个1
deque<int> d2=d;  //赋值为另一个队列
deque<int> d3;
d3.assign(d.begin(), d.end());  //赋值为另一个队列的一部分


//没有容量(capacity)
bool empty = d.empty(); //是否空
int size = d.size();  //大小, 包含的数据数量
d.resize(10, 0);  //修改数组大小, 比原来小了会删除超出的部分, 比原来大了会填充0

插入删除-存取

deque<int> d;
d.push_back(1);  //尾部添加
d.push_front(1);  //头部添加
d.pop_back();  //尾部删除
d.pop_front();  //头部删除
d.insert(d.begin(), 1);  //在开头插入1, 开头插入需要移动所有数据效率低
d.insert(d.begin(), 2,1);  //在开头插入两个1
d.insert(d.begin(), d.begin()+1,d.end());  //在开头插入一个区间
d.erase(d.begin());  //删除开头的
d.erase(d.begin(), d.end());  //删除区间[begin, end)  左闭右开,  v.erase(v.begin(), v.end())->v.clear();
d.clear();  //删除全部

int num = d[0];  //获取第一个元素
int num = d.at(0);  //获取第一个元素
int front = d.front(); //获取第一个元素
int back = d.back();  //获取最后一元素


排序

#include <algorithm>
deque<int> d {2,1,6,8};
//支持随机访问的才可以用标准算法algorithm
sort(d.begin(),d,end()); //#include <algorithm>, 排序

deque<int> d{1,2,5};
d.sort(compare<int>);  //降序

template<typename T>
bool compare(T v1, T v2){
  return v1>v2;
}

stack

先进后出, 栈顶进栈顶出, 不能遍历

#include <stack>

stack<int> s;
s.push(1);  //入栈
s.pop(); //出栈
int top = s.top();  //查看栈顶元素

queue

先进先出, 只可访问头尾, 尾入(push)头出(pop)

#include <queue>

queue<int> q;
q.push(1);  //入队
bool empty = q.empty();
int front = q.front();  //获取队头
int back = q.back();  //获取队尾
q.pop(); //出队
int size = q.size();  //大小

list链表

插入删除快, 访问慢,占用空间大

构造-赋值-大小

#include <list>
//构造方式与其他容器一样

list<int> l;  //创建空list
list<int> l {1,2,3,4,5};  //直接初始化值
l.push_back(1);
list<int> l(l.begin(),l.end());
list<int> l(10,1);
list<int> l(l);

list<int> l2 = l;
list<int> l3;
l3.assign(l2.begin(),l2,end());
l3.assign(10,1);

l2.swap(l3);  //交换值

bool empty = l.empty();
l.size();  //元素个数
l.resize(10);  //大了会默认0填充, 小了会删除多的

插入删除-存取

list<int> l;
l.push_back(1);
l.push_front(2);
l.pop_front();
l.pop_back();
l.insert(l.begin(), 1);
l.insert(l.begin(), 2,1);  //在开头插入两个1
d.insert(l.begin(), l.begin()+1,l.end());  //在开头插入一个区间

l.erase(l.begin());  //删除开头
l.erase(l.begin(), l.end());  //删除区间[begin, end)  左闭右开,  v.erase(v.begin(), v.end())->v.clear();
l.remove(100);  //删除所有值为100的
l.clear();  //清空

l.front();  //只能获取开头或结尾的值
l.back();
//不能通过下标访问,只能通过迭代器自增访问所有值
for (list<int>::iterator it = l.begin(); it != l.end(); it++) {
  cout << *it << " ";
}

排序-反转

list<int> l {1,2,3};
l.reverse();
l.sort();  //升序, 因为不支持随机访问不能使用stl算法
l.sort(compare)

l.sort(compare<int>);  //降序

template<typename T>
bool compare(T v1, T v2){
  return v1>v2;
}

set/multiset

关联式容器,自动排序,二叉树实现, set不能重复, multiset能重复其他操作一样

构造-赋值-大小

#include <set>

set<int> s{1,5,2};
s.insert(1);  //set插入返回pair<iterator,bool>数据, multiset插入返回迭代器
set<int> s2{2,3,6};
s2.swap(s);
s.size();   //不能resize

遍历

set<int> s{1,5,2};
for (set<int>::iterator it = s.begin(); it != s.end(); it++) {
  cout << *it << " ";
}

插入删除

set<int> s{1,5,2};
s.insert(1);  //插入
s.erase(s.start());
s.erase(s.start(),s.end());
s.erase(10);  //删除值为10的
s.clear();

查找-统计

set<int> s{1,5,2};
s.find(1);  //查找元素,返回对应位置的迭代器, 不存在返回s.end();
s.count(1); //计算值为1的数量, multiset可以有多个重复值

pair

pair<int,int> p(1,1);
pair<int,int> p = make_pair(1,1);
p.first;  //获取第一个值
p.second;  //获取第二个值

排序

默认升序排序

set<int Compare_c <int>> s {1,2,3};  //逆序排序,使用仿函数,指定排序规则
//Compare_c 要使用类,是因为模板参数是类型而不是函数, 但是Compare_c 又需要像函数一样调用,就需要使用仿函数
template<class T>
class Compare_c {
public:
  bool operator()(T v1, T v2) const{  //必须加const不然会报错
    return v1 > v2;
  }
};

map/multimap

高性能,高效率,pair键值对, 自动排序

构造-大小

#include <map>
map<int,int> m;
map<int, int> m{{1,2},{2,3},{3,5}};
m.insert(pair<int,int>(1,2));
map<int,int> m2(m);
m.size();
m.empty();
m.swap(m2);

遍历

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

插入-删除

map<int,int> m;
m.insert(pair<int,int>(1,2));
m.insert(make_pair(1,2));  //make_pair不需要写类型
m.insert(map<int,int>::value_type(2,10));
m[3]=10;  //可用于创建或访问, 访问时不存在也会创建一个为0的值

m.erase(m.begin());  //删除开头的
m.erase(3);  删除key为3的
m.erase(m.begin(),m.end());  //删除区间
m.clear();  //清空

查找-统计

map<int,int> m;
m.find(1);  //查找key为1的,返回对应迭代器,不存在返回m.end(), 用迭代器获取键和值
m.count(1);  //计算key为1的数量


//multimap查找特定数据
multimap<int, Worker>::iterator it = m.find(0);
int count = m.count(0);  //查找所有key为0的
for (int i=0; i<count; it++,i++) {
  cout << it->first << ": " << it->second.name << " " << it->second.salary << "\n";
}

排序

template<class T> class Compare_c {
public:
  bool operator()(T v1, T v2) const{
    return v1 > v2;  //>为递减,  <为递增
  }
};

map<int, int, Compare_c<int>> m{{1,2},{2,3},{3,5}};   //使用自定义排序规则

仿函数

可以通过属性记录状态, 可以作为参数传递,

class fun {
public:
  int count{ 0 };
  void operator()(string s) {
    cout << s << endl;
    count++;  //通过属性记录状态
  }
};

fun p;
p("print");


fun()("print");  //匿名函数

谓词

仿函数返回值是bool类型, 一个参数就是一元, 两个参数就是二元

一元

class fun {
public:
  bool operator()(int s) {  //一元谓词
     return s>5;
  }
};


vector<int> v{1,2,3,4,5,6,7,8,9};
vector<int>::iterator it = find_if(v.begin(),v.end(), fun());  //寻找是否有>5的
if(it==v.end()){
  cout<<"not find"<<"\n";
}
else{
  cout<<*it<<endl;
}

二元

class SortCompare{
 public:
   bool operator()(int v1,int v2){
     return v1>v2;
   }
}
vector<int> v{1,2,3,4,5,6,7,8,9};
sort(v.begin(),v.end(), SortCompare());

内建仿函数

算数仿函数

#include <functional>

negate<int> n;  //取反
n(50);  //-50

plus<int> p;  //求和
p(1,2);  //3

关系仿函数

#include <functional>
vector<int> v{1,2,3,4,5,6,7,8,9};
sort(v.begin(),v.end(), greater<int>());   //内建比较二元谓词 >   默认是less <

逻辑仿函数

#include <functional>
#include <algorithm>
vector<bool> v{true,false,true,true};
vector<bool> v2;
v2.resize(v.size());  //把v的值放到v2并取反
transform(v.begin(),v.end(),v2.begin(), logical_not<bool>());


常用算法

algorithm 比较, 运算, 查找, 遍历, 复制, 修改

functional 序列运算

numeric

遍历

#include "algorithm"

//for_each-----------------
vector<int> v{1,2,3,4,5};
for_each(v.begin(),v.end(),print_fun);
for_each(v.begin(),v.end(),print_cls());

void print_fun(int v){
  cout<<v<<endl;
}
class print_cls{
public:
  void operator()(int v){
    cout<<v<<endl;
  }
};

搬运

#include "algorithm"
vector<int> v{1,2,3,4,5};
vector<int> v2(5,0);
transform(v.begin(),v.end(),v2.begin(), transform_fun);
transform(v.begin(),v.end(),v2.begin(), transform_cls());

int transform_fun(int v){
  return v;
}
class transform_cls{
public:
  int operator()(int v){
    return v;
  }
}

查找

#include <algorithm>
#include <vector>
#include <string>
class Person {
public:
  string name;
  int age;
  bool operator==(string name) const {  //查找时需要与find的第三个参数比较, 返回bool类型
    return this->name == name;
  }
  Person(string name, int age) {
    this->name = name;
    this->age = age;
  }
};


class find_cls {
public:
  bool operator()(const Person& p1) const {
    return p1.age > 2;
  }
};

bool sort_fun(const Person& p1, const Person& P2){
  return p1.age<p2.age;
}
int main() {
  vector<Person> v;
  char names[]{ 'a','b','c','d','e' };
  for each (char s in names) {
    string name = "person";
    name += s;
    Person p(name, 10);
    v.push_back(p);
  }
  //查找自定义对象, 未找到返回v.end()
  vector<Person>::iterator it_find = find(v.begin(), v.end(), "persona");  
  std::cout << it_find->name;

  //根据find_cls仿函数的条件查找自定义对象, 返回第一个, 未找到返回v.end()
  vector<Person>::iterator it_findif = find_if(v.begin(), v.end(), find_cls());  
  std::cout << it_findif->name;
  
  //查找相邻重复元素, 未找到返回v.end()
  vector<Person>::iterator it_findadjacent = adjacent(v.begin(), v.end(), find_cls());
  
  //binary_search  二分查找, 对象有序
  vector<int> v2 = { 1,2,3,4,5,6 };
  bool exist = binary_search(v2.begin(), v2.end(), 3);
  
}

统计个数

#include <algorithm>
#include "vector"

class Person {
public:
  string name;
  int age;
  bool operator==(const Person& p) const {  //查找时需要与find的第三个参数比较, 返回bool类型
    return this->age == p.age;
  }
  Person(string name, int age) {
    this->name = name;
    this->age = age;
  }
};

//计数条件
class count_cls {  //必须放在Person下边
public:
  bool operator()(const Person& p1) const {
    return p1.age > 2;
  }
};

vector<int> v1{1,2,3,45,2,3};
int count_num = count(v1.begin(),v1.end(), 2);

vector<Person> v2;
char names[]{ 'a','b','c','d','e' };
for each (char s in names) {
  string name = "person";
  name += s;
  Person p(name, 10);
  v2.push_back(p);
}
int count_p = count(v2.begin(),v2.end(), Person("name", 10));   //统计自定义类型数量

int countif_p = count_if(v2.begin(),v2.end(), count_cls());  //条件统计数量

排序

#include <algorithm>
//使用上面定义的Person 数组

bool sort_fun(int v1, int v2){
  return v1>v2;
}

sort(v.begin(),v.end(), sort_fun);  //自定义排序
random_shuffle(v.begin(), v.end());  //打乱数组


合并

合并两个相同排序的容器

#include <algorithm>
vector<int> v1{1,2,3};
vector<int> v2{3,4,5};
vector<int> v3;
v3.resize(6);

merge(v1.begin(),v1.end(),v2.begin(),v2.end(),v3,begin());

反转

#include <algorithm>
vector<int> v1{1,2,3};
reverse(v1.begin(),v1.end())

拷贝和替换

#include <algorithm>
vector<int> v1{1,2,3};
vector<int> v2;
v2.resize(3);
copy(v1.begin(),v1.end(), v2.begin());
replace(v1.begin(),v1.end(), 1, 6);  //把区间里的1替换为6

class replace_cls {  //必须放在Person下边
public:
  bool operator()(int num) const {
    return num > 2;
  }
};

replace_if(v1.begin(),v1.end(),replace_fun , 6);  //把区间里的>2的替换为6
swap(v1,v2);  //交换容器

算数

#include "numeric"
vector<int> v1{1,2,3};
int total = accumulate(v.begin(),v.end(), 0);  //累加, 0起始累加值

填充

#include "numeric"
vector<int> v;
v.resize(10);
v.fill(v.begin(),v.end(), 100);  //填充区间值为100

集合算法

#include <algorithm>
vector<int> v1{1,2,3};
vector<int> v2{2,3,4,5};
//交集------------
vector<int> v3;
v3.resize(min(v1.size(),v2.size()));  //min->#include <algorithm>
set_intersection(v1.begin(),v2.begin(),v2.begin(),v2.end(), v3.start()); 
//v1,v2的交集赋值给v3, 返回v3最后一个值的迭代器
//并集------------
vector<int> v4;
v3.resize(v1.size()+v2.size()); 
set_union(v1.begin(),v2.begin(),v2.begin(),v2.end(), v4.start());
//v1,v2的并集赋值给v4, 返回v4最后一个值的迭代器
//差集------------
vector<int> v5;
v5.resize(v1.size())
set_difference(v1.begin(),v2.begin(),v2.begin(),v2.end(), v5.start())
//v1和v2的差集, v1减去v2中的值,赋值给v5, 返回v5最后一个值的迭代器

  • 21
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值