C++看这个

文章目录


一、数据类型

1、const关键字

1、const以常量初始化,修饰的只读变量,变量值会存放在符号常量表中,不会立即给data开辟空间,取地址时才开辟空间。

 	const int data=100;//以常量初始化
    int *p = (int *)&data;
    *p=200;
    cout<<"*p="<<*p<<endl;//200
    cout<<"data="<<data<<endl;//100

2、const以变量初始化,修饰的只读变量,会立即开辟空间。

	int data1=1000;
    const int  data2=data1;//以变量初始化
    int *p1= (int *)&data2;
    *p1=2000;
    cout<<"*p1="<<*p1<<endl;//2000
    cout<<"data2="<<data2;//2000

3、const 修饰常量指针,指针的指向可以修改,指向的值不可以修改

int a = 10;
int b = 20;
const int * p = &a;

*p=11;//错误,指向的值不能修改
p=&b;//正确,指向可以修改

4、const修饰指针常量,指针的指向不可以修改,指向的值可以修改

int a = 10;
int b = 20;
 int * const p = &a;

*p=11;//正确,指向的值可以修改
p=&b;//错误,指向不可以修改

2、volatile关键字:强制从内存访问

3、typedef 取别名。

 typedef int INT;
 INT data=1;

 typedef int ARRAY[3];
 ARRAY arr={"1","2","3"}


typedef int *MYP;//指针类型
MYP p;
 

4、static 定义的静态变量,只在自己的{ }内有效; 静态函数 就近原则。

在这里插入图片描述

二、运算符

1./:除号

5 / 2 == 2
5 / 2.0 == 2.5

2.随机数

rang()函数取 最大数和最小数的差再加一的余数,再加上最小数

产生一个100~200的随机数
int a = rand()%101+100

产生一个a~z的随机字母。
char ch = rand()%26+'a';



#include <time.h>
void testrand(){
    //获取随机数种子,time (NULL)获取当前时间
    srand(time(NULL));

    cout<<rand()<<endl;
}

3.遍历二位数组

void testArrylist02(){
    int arr[3][3]={19,27,32,46,87,32,45,13,54};

    float scoreavg[3]={0.0f};

    for (int i=0;i<3;i++) {

        int sum=0;

        for (int j=0;j<3;j++) {

            sum+=arr[i][j];

        }
        scoreavg[i]=sum/3;

        cout<<scoreavg[i]<<endl;
    }
}

三、指针

1、指针数组

数组的每个元素为指针

	char *a[3];

2、数组指针

本质是指针变量,保存的是数组的首地址

int arr[5]={10,20,30,40,50}
int (*p)[5]=&arr;//p=&arr,*p=*&arr=arr,*(*p+2)=30

3、一维数组做函数参数,会被编译器优化成,指针变量

4、函数指针变量,使用typedef

int add(int x,int y){
    return x+y;
}

void testadd(){
    //给函数指针类型取别名
    typedef int (*ADD)(int x,int y);
    ADD p=NULL;

    p=add;
    cout<<p(10,20)<<endl;
}

四、字符串长度,复制

1.strlen和sizeof的区别

在这里插入图片描述
sizeof: 是运算符,以字节为单位,计算机占用内存大小,在编译时处理,处理的参数为类型,对象
strlen: 是函数,计算字符串的长度,在运行时处理,处理的参数类型为指针,数组,并且要有字符串的头文件

2、字符串覆盖strncpy

//把d数组的前3位,覆盖到e数组的前3位。
    char c[100]="12345";
    char d[5]="6789";
    strncpy(c,d,3);
    cout<<c<<endl;//67845

3、字符串追加 strncat()

 //把f数组的前3位追加到e数组。
    char e[100]="12345";
    char f[5]="6789";
    strncat(e,f,3);
    cout<<e<<endl;//12345678

4、字符串比较 strcmp()和strncmp()

/*
     * >0:s1字符串>s2字符串
     * <0:s1字符串<s2字符串
     * =0:s1字符串=s2字符串
    */
    //字符数组
    char g[5]="abcd";
    char h[5]="abcc";
    int a=strcmp(g,h);
    int num=strncmp(g,h,4);//比较前4位
    cout<<num<<endl;
	
	//字符串
    string name1 = "zhangsan";
    string name2 ="lisi";

    int result=name1.compare(name2);
    cout<<result<<endl;

    cout<<sizeof (name2)<<endl;//24
    cout<<name1.size()<<endl;//8
    cout<<name2.length()<<endl;//4

五、按位运算

1.按位与

按位与&:有0为0,全1为1

在这里插入图片描述

2.按位或

有1为1,全0为0

在这里插入图片描述

3.按位异或and按位取反

~按位取反,0变1,1变0;
^按位异或相同为0,不同为1

在这里插入图片描述

六、C++基础知识

1、作用域

局部变量和全局变量同名,要在局部类或方法使用全局变量,加上 ::

2、命名空间

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

using声明

在这里插入图片描述

先找局部,再找全局,再到命名空间去找

在这里插入图片描述

3、结构体

C定义结构体变量需要加struct关键字,C++不需要
C结构体只能定义成员变量,C++既可以定义成员变量,还可以定义成员函数

4、引用

1、变量的引用

#include <iostream>
using namespace std;
void testyinyong(){
    int a=10;

    //给变量a 取别名b, &修饰变量为引用,b就是a 的引用(别名)
    //系统不会为b开辟空间
    int &b=a;//引用必须初始化

    cout<<b<<endl;
}

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

2、数组的引用

//数组的引用
void testarray(){

    int arr[3]={2,56,32};

    int n=sizeof(arr)/sizeof(arr[0]);

    int (&myarr)[3]=arr;

    for(int i=0;i<n;i++){

        cout<<(myarr)[i]<<endl;
    }
}

3、指针的引用

//指针的引用
void testPointer(){
    int num=10;

    int *p=&num;

    //指针p取别名
    int* &myp=p;

    cout<<"*p="<<*p<<endl;//10
    cout<<"*myp="<<*myp<<endl;//10

}

4、函数的引用

//函数的引用
void Fun(int i){

    cout<<i;

}

void (&myFun)(int a)=Fun;

int main()
{

    myFun(2);
    
    return 0;
}

5、引用作为函数的参数

//引用作为函数的参数
void Fun01(int &x,int &y){
    int z=x;
    x=y;
    y=z;

}

int main()
{
    int a=11;
    int b=22;
    
    Fun01(a,b);
    cout<<"a="<<a<<"  "<<"b="<<b<<endl;
    
    return 0;
}

5、内联函数(编译阶段,函数体替换函数调用处,避免函数调用的开销)

1.在定义时用inline修饰,不能在声明的时候修饰

//声明函数
int add(int x,int y);
int main(){
	int a=3;
	int b=4
	cout<<add(a,b);
}

//定义函数
inline int add(int x,int y){
	return x+y;
}

2.内联函数注意实现

1.在内联函数定义的时候加inline修饰
2.类中的成员函数默认都是内联函数(不加inline也是内联函数)
3.有时候就算加上inlinet也不一定是内联函数(内联函数条件)
不能存在任何形式的循环语句
不能存在过多的条件判断语句
函数体不能过于庞大
不能对函数取地址
4.有时候不加inline修饰也有可能是内联函数。
5.内不内联由编译器决定.

6、#if,#endif

C++的条件编译预处理命令,必须以结束 #endif 指令与源文件中的每个 #if 指令。任意数量的 #elif 指令可以出现在 #if 和 #endif 指令之间,但是,最多一个 #else 指令允许。#else 指令,如果有,则必须是最后一个指令

有两种格式:

1:#ifdef 标示符

程序段1

#else

程序段2

#endif

表示:如果标示符已经被#define命令定义过,则编译程序段1,否则编译程序段2

2:#if 表达式

程序段1

#else

程序段2

#endif

表示:如果表达式为真,则编译程序段1,否则编译程序段2.

7、析构函数(当类中有指针成员,这个类必须写)

函数名和类名相同,函数名前加~,没有返回值类型,没有形参,不能被重载,当对象生命周期结束后,系统会自动调用析构函数,再释放内存空间。

在这里插入图片描述

#include <iostream>
#include <string.h>
using namespace std;

class Date{
  public :
    char *name;
public:
    Date(){
        name=NULL;
    }
    Date(char *str){
        name =new char [strlen(str)+1];
        strcpy(name,str);

    }
    ~Date(){
        if(NULL!=name){
            delete [] name;
            name=NULL;
        }
    }
};
int main()
{
    Date stu("guoc");
    cout<<stu.name;
    return 0;
}

8.拷贝构造函数以及深拷贝(深拷贝括号里要用引用)

1.旧对象给新对象初始化时,就会调用拷贝构造函数
2.深拷贝(要用引用):为对象的指针成员申请独立的空间
3、qt处理返回对象时:外界对象接管函数内部返回的对象,没有拷贝构造提高了效率,不会创建匿名对象提高效率

#include <iostream>
#include <string.h>
using namespace std;


class Date{
  public :
    char *name;
public:
    Date(){
        name=NULL;
    }
    Date(char *str){
        name =new char [strlen(str)+1];
        strcpy(name,str);

    }

    //深拷贝(括号里要用引用)
    Date(const Date &stu){
        //为对象的指针成员申请独立的空间
        name=new char [strlen(stu.name)+1];
        strcpy (name,stu.name);
    }

    //析构函数(有指针成员时,必须写)
    ~Date(){
        if(NULL!=name){
            delete [] name;
            name=NULL;
        }
    }
};
int main()
{
    Date stu("Hello World!!!");
    cout<<stu.name;
    return 0;
}

9、对象成员以及初始化列表

#include <iostream>
using namespace std;

class A{
public:
    int Aage;
public:
    A(){
        Aage=0;
    }
    A(int a){
        Aage=a;
    }
    ~A(){

    }
};

class B{
public:
    int Bage;
    A guoc;//成员对象
public:
    B(){

    }

    //初始化列表,成员对象,使用 对象名+()
    B(int a,int b):guoc(a){
        Bage=b;
    }
    ~B(){

    }
};
int main()
{
    B xuanz(21,22);
    cout<<"Aage="<<xuanz.guoc.Aage<<"  Bage="<<xuanz.Bage<<endl;
    return 0;
}

10.explicit关键字

explicit关键字,修饰构造函数是禁止构造函数进行隐式转换,一般是针对单参数构造函数,或一个参数,其他参数有默认值的构造函数

在这里插入图片描述

11、static关键字

1、static修饰的静态成员,属于类不属于对象(所有对象共享一份静态成员数据)。
2、在定义类时,静态成员就分配空间。
3、静态成员必须在类中定义,类外初始化, 当然啦类外则不用再加static关键字。
4、静态成员函数,只能操作静态成员变量;非静态成员函数,可以调用类的静态成员函数和静态变量(因为静态成员函数内部没有this指针).

#include <iostream>
#include <bitset>
using namespace std;

//static关键字
class A
{
public:
    int x=10;
    //静态成员类中定义
    static int y;
};
//静态成员类外初始化, 类外则不用再加static关键字
int A::y=20;

void testStatic(){
    cout<<A::y<<endl;

    A guoc;
    cout<<guoc.y;
}
int main()
{
    testStatic();
    return 0;
}

12.const修饰成员函数(只读状态)

在成员函数体内,不可以修改本类中任意的普通成员变量,当成员变量类型为mutable时除外

在这里插入图片描述

13、友元

1.使用friend关键字在一个类或函数中,声明一个类或函数,那么这个类或函数可以直接访问这个类或函数的私有数据。
2.使用规则:将作为友元的类或函数的声明放在最前面,友元的初始化实现放在最后面。
3.注意事项:友元关系不能被继承;友元的关系是单向的;并且不具有传递性;

在这里插入图片描述

14、全局函数重载

1、输出运算符重载 <<

#include <iostream>
#include <string>
using namespace std;

class Person{
    friend ostream& operator<<(ostream &out,Person &ob);
private:
    int num;
    string name;
    float score;
public:
    Person(){}

    //有参构造函数,初始化列表
    Person(int num,string name,float score):num(num),name(name),score(score){}
};

//重载全局函数,这里重载 operator<<
ostream& operator<<(ostream &out,Person &ob){
    out<<"学号:"<<ob.num<<" 姓名:"<<ob.name<<" 分数:"<<ob.score<<endl;
    return out;
}

int  main(int argc, char *argv[])
{
    Person cat(002,"tom",88.9);

    cout<<cat;
    cout<<cat<<endl; 

    return 0;
}

2、重载 operator>>

//重载 operator>>
istream& operator>>(istream &in, Person &ob){

    in>>ob.num>>ob.name>>ob.score;
    return in;
}

15、智能指针(智能指针,相当于定义一个公共的,会释放内存的指针)

#include <iostream>
using namespace std;

class Date{
public:
    void func(){
        cout<<"智能指针"<<endl;
    }
};

//智能指针,相当于定义一个公共的,会释放内存的指针
class SP{
public:
    Date *p;
public:
    SP();

    SP(Date *dp){
        this->p=dp;
    }
    ~SP(){
        delete p;
    }

    //重载->运算符
    Date* operator->(){
        return p;
    }
    
    //重载* 
    Date& operator*(){
        return *p;
    }
};
int main()
{
//生成一个SP类的指针对象,指向Date对象
    SP sp=new Date;

    sp->func();
    (*sp).func();
    
    return 0;
}

16、继承

1.继承中的构造顺序,注意看是父类的类名还父类的对象名

在这里插入图片描述

2、继承中同名处理

(1)子类和父类的同名处理:添加作用域。
(2)子类和父类的同名成员处理:子类默认优先访问子类同名成员,加作用域才会访问父类同名成员。

3、菱形继承,虚继承

在这里插入图片描述

17、多态

1、静态多态(编译阶段,早绑定):函数重载,运算符重载,重定义。
2、动态多态(运行阶段,晚绑定):虚函数。

3、虚函数(是被virtual修饰的成员函数,会产生一个虚函数指针vfptr,指向虚函数表,如果这个类没有涉及到继承,这时虚函数表记录的就是当前虚函数的入口地址)。

4、
重载:无继承,同一作用域,参数的个数不同、顺序不同、类型不同都可重载。
重定义:有继承,子类重定义父类的同名非虚函数参数顺序,个数,类型可以不同),子类一旦重定义了父类的同名函数(不管参数是否一致),子类中将屏蔽父类所有的同名函数。
重写(覆盖):有继承,子类 重写父类的虚函数,返回值类型,函数名,参数顺序,个数,类型必须一致。

5、纯虚函数

定义方式

class Animal
{
public:
    Animal() {}

    //纯虚函数
    virtual void speak(void)=0;
};
  1. 一旦这个类有纯虚函数,那么这个类,就是抽象类
    2.抽象类,不能实例化对象
    3.抽象类必须被继承,同时子类 必须被重写父类所有纯虚函数,否则子类也是抽象类。

6、虚析构函数 和 纯析构函数(作用上没什么区别,都是通过父类指针,来释放整个子类空间,定义方式不同, 而且这个类有纯析构函数,那么这个类,就是抽象类)

1、虚析构: 通过父类指针,释放整个子类空间。

#include <iostream>
using namespace std;

//虚函数
class Animal02
{
public:
    Animal02() {
        cout<<"Animal02构造实现了"<<endl;

    }
//虚析构:通过父类指针,释放整个子类空间
    virtual ~Animal02(){
        cout<<"Animal02析构实现了"<<endl;
    }

    virtual void speak(void){
        cout<<"叫了一声"<<endl;
    }
};

class Cat:public Animal02
{
public:
    Cat(){
        cout<<"Cat构造实现了"<<endl;
    }
    ~Cat(){
        cout<<"Cat析构实现了"<<endl;
    }

    void speak(void){
        cout<<"喵喵"<<endl;
    }
};

void animalSpeak(Animal02 *p){
    p->speak();
    delete p;
}

int main()
{
    //虚函数
    animalSpeak (new Cat);//通过父类指针,调用子类函数

    return 0;
}

2.纯虚析构函数:处理各类的回收工作,不能被继承,并且得有函数体,以及要在类外实现

#include <iostream>

using namespace std;

class Animal01
{
public:
    Animal01(){
        cout<<"Animal01构造 实现了"<<endl;
    }

    //纯虚析构,必须在类外实现
    virtual ~Animal01()=0;

    //纯虚函数
    virtual void speak(void)=0;
};

//纯虚析构的类外实现
Animal01::~Animal01(){
     cout<<"Animal01纯虚析构,实现了"<<endl;
}

class Dog:public Animal01
{
public:

    Dog(){
        cout<<"Dog构造实现了"<<endl;
    }

    ~Dog(){
        cout<<"Dog析构实现了"<<endl;
    }

    virtual void speak(void){
        cout<<"叫了汪汪!"<<endl;
    }
};

int main()
{

    //纯虚函数部分,包括虚析构
    Animal01 *a=new Dog;
    a->speak();
    delete a;

    return 0;
}

18、模板

1、函数模板

#include <iostream>
using namespace std;

//T只对当前下面的函数有效
template <typename T>
void swapAll(T &a,T &b){
    T tmp = a;
    a=b;
    b=tmp;
    return;
}


void swapAll(int &a,int  &b){
    int tmp = a;
    a=b;
    b=tmp;
    return;
}


//第二部分
class Stu{
    friend ostream& operator<<(ostream &out,Stu stu);

private:
    string name;
public:
    Stu(){}

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

template <typename T>
void myPrint(T a){
    cout<<a<<endl;
}

ostream& operator<<(ostream &out,Stu stu){
    out<<stu.name<<endl;
    return out;
}

int main()
{
    int x=4,y=5;

    swapAll(x,y);//会优先使用 普通函数

    //函数调用时,根据实参类型,会自动推到T的类型
    swapAll<>(x,y);//加 <> 会优先使用 函数模板
    //cout<<"x= "<<x<<" y= "<<y<<endl;


    //第二部分,对于 T 为数组或其他自定义类,可能导致运算符不能识别
    Stu guoc("郭超");
    myPrint(guoc);
    return 0;
}

2、类模板

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

19、类型转换

1、static_cast 静态类型转换(支持3中:基, 上 ,下)

#include <iostream>
using namespace std;

class Father{
public:
    double a=3.14;
};

class Son:public Father{
public:
    //基本类型转换:支持,安全,
    int num = static_cast<int>(a);
};

int main()
{
    //1.static_cast 静态类型转换
        //上行转换:支持,安全
    Father *p = static_cast<Father *>(new Son);
    cout<<p->a<<endl;

        //向下转换:支持,不安全
    Son *sp = static_cast<Son *>(new Father);
    cout<<sp->num<<endl;
    cout<<sp->a<<endl;
        return 0;
}

2、dynamic_cast 动态类型转换(支持1种:上)

//2.dynanic_cast 动态类型转换
        //上行转换:支持
    Father *fp = dynamic_cast<Father *>(new Son);
    cout<<fp->a<<endl;
    

3、interpret_cast 重新解释转换(支持3种:基指针 ,上 ,下)

 //3、reinterpret_cast重新解释转换
        //1.基本类型:不支持
    //int num =reinterpret_cast<int>(3.14f);

        //2.基本指针类型:支持
    float *q;
    int *fq = reinterpret_cast<int *>(q);

        //3.向上转换:支持
    Father *fp2 = reinterpret_cast<Father *>(new Son);

        //4.向下转换:支持‘
    Son *sp2 = reinterpret_cast<Son *>(new Father);

4、const_cast 常量转换(两种切换:指针,引用)

1、将const 修饰的 指针,引用 转换为 非const修饰的

//1、将const 修饰的 指针和引用,转换为 非const修饰的
    int cc2[3]={10,20,30};

    const int *cp1=cc2;
    int *cp2= const_cast<int *>(cp1);

    const int &ob1=cc2[1];
    int &ob2=const_cast<int &>(ob1);

2、将非const 修饰的 指针,引用 转换为 const修饰的

//2、将非const 修饰的 指针,引用 转换为 const修饰的
    int cc3=20;

    int *cp3=&cc3;
    const int *cp4= const_cast<const int *>(cp3); 
    
    int &ob3=cc3;
    const int &ob4=const_cast<const int &>(ob3);

20、异常(catch()里要用引用)

1、 栈解旋:从进入try块起,到异常抛出前,这期间在栈中所有构造的对象,都会自动析构。

2、 异常的多态

#include <iostream>
#include <exception>
using namespace std;

//异常的多态
class BaseException{
public:
    virtual void printError(){}

};

//空指针异常
class NullPointerException:public BaseException{
public:
    virtual void printException(){
        cout<<"空指针异常!!"<<endl;

    }
};

//越界异常
class OutofRangeException:public BaseException{
public:
    virtual void printError(){
        cout<<"越界异常!!!"<<endl;
    }
};

void dowork(){
    throw OutofRangeException();
}
};

int main()
{
    try {
        dowork();
    } catch (BaseException &e) {
        e.printError();
    }
    return 0;
}

3、如何编写自己定义的异常
注意:要基于标准异常基类,编写自己的异常类,继承exception

#include <iostream>
#include <exception>
using namespace std;

//编写自己的异常,基于标准异常基类,编写自己的异常类
class NewException:public exception
{
private:
    string msg;
public:
    NewException(){}
    NewException(string msg){
        this->msg=msg;
    }

    //重写父类的what方法
    virtual const char* what() const throw()//防止父类在子类前抛出标准异常
    {
        //将string类型转换成char*
        return this->msg.c_str();
    }

    ~NewException(){}
};

int main()
{
    //自己的异常
    try {
        throw NewException("自己的异常");
    } catch (exception &e) {
        e.what();
        cout<<e.what();
    }
    return 0;
}

七,STL容器

1.STL容器六大组件

1.容器:存放数据
2.算法:操作数据(质算法:更改区间元素内容,如:拷贝替换删除。
非质算法:没更改区间元素内容,如:查找,遍历,计数等)
3.迭代器:算法通过迭代器操作容器数据
4.仿函数:为算法提供更多策略
5.适配器:为算法提供更多参数接口
6.空间配置器:为算法和容器 动态分配、管理空间.
在这里插入图片描述

2.StringAPI (注意一定要有string的头文件)

1.string 的 声明 和 查询

#include <iostream>

//注意要有string的头文件
#include <string>
using namespace std;

//声明和查询
void defineSelect(){
    string guoc="hello world!!!";
    cout<<guoc[3]<<" "<<guoc.at(3)<<endl;

    guoc[6]='W';
    guoc.at(0)='H';
    cout<<guoc<<endl;

    //注意:[]越界不会抛异常,  at方法越界会抛出异常
    try {
        throw guoc.at(20)='A';
    } catch (exception &e) {
        cout<<e.what();
    }
}
int main()
{
    defineSelect();
    return 0;
}

2、字符串 的 连接(直接用+符号,或者append函数( append:附加,增加))

#include <iostream>

//注意要有string的头文件
#include <string>
using namespace std;

void joint(){
    string str1="hello";
    str1+="world";
    cout<<str1<<endl;


    string str2="guoc say:";
    str2+=str1;
    cout<<str2<<endl;


    string str3="abcd";
    string str4="ABCD";

    str3.append(str4);//4追加到3末尾
    cout<<str3<<endl;//abcdABCD
    str3.append("efgh",2);//efgh前两位追加到3
    cout<<str3<<endl;//abcdABCDef

    string str5="abcd";
    string str6="ABCD";
    str5.append(str6,2,3);//6的下标从2到3,追加到5上
    cout<<str5<<endl;//abcdCD
}
int main()
{  
    joint();
    return 0;
}

3、string 字符串的比较 ( 直接> ,< , =, 或者 compare()函数 )
1.= 返回0
2.> 返回1
3.< 返回-1

#include <iostream>
//注意要有string的头文件
#include <string>
using namespace std;

void testCompare(){
    string str7="haha";
    string str8="abcd";
    int outcome1=str7.compare(str8);
    int outcome2=(str7>str8);//重载了:= > <
    cout<<outcome1<<endl;
    cout<<outcome2<<endl;
}
int main()
{  
    testCompare();
    return 0;
}

4、截取,提取子字符串( substr()函数 )

#include <iostream>
#include <string>
using namespace std;

void extract(){
    string str9="111:222:333:444";
    int pos=0;
    while(1)
    {
        int ret=str9.find(":",pos);
        if(ret<0){
            cout<<"无符合要求"<<endl;
            break;
        }
        string tmp =str9.substr(pos,ret-pos);
        cout<<tmp<<endl;
        break;
   }
}
int main()
{  
    extract();
    return 0;
}

5、字符串的插入和删除( inster()函数 和 erase()函数 )

#include <iostream>
#include <string>
using namespace std;

void insertDelete(){
   string str10="limeng";
   str10.insert(2,"meng666");//从下标为2,开始插入
   cout<<str10<<endl;//limeng666meng

   str10.erase(6,3);//从 下标为6开始,删除3个字符
   cout<<str10<<endl;
}
int main()
{  
    insertDelete();
    return 0;
}

7、字符串的类型转换:string需要成员函数 c_str() , 转成 char *

#include <iostream>
#include <string>
using namespace std;

void cstyleChange(){

    //string需要成员函数c_str(),转成char *
    string str11="limengmeng";
    char *name1=const_cast<char *>(str11.c_str());

    //为什么只输出指针名就可以打印出整个字符串?
    //因为打印时遇到 \0 ,才会自动结束
    cout<<name1<<endl;
    cout<<&name1<<endl;//虽然指针名就代表的是地址,但在输出地址时需要加&

   //char *转成string(默认支持)
    const char *name2="guoc";
    string str12=name2;
    cout<<str12<<endl;
}
int main()
{  
    cstyleChange();
    return 0;
}

3、Vector容器

1、Vector容器介绍:是单向线性连续内存空间,动态增加大小(未雨绸缪机制)是原空间大小满载后,会开辟一个原空间2倍大小的新空间,将原空间内容拷贝到新空间,再释放原空间内存。

在这里插入图片描述

#include <iostream>
//注意头文件
#include <vector>
using namespace std;

void test01(){
    vector<int> v1;
    //预留50的空间大小
    v1.reserve(50);
    cout<<sizeof (v1)<<endl;
    cout<<"容量"<<v1.capacity()<<"  大小:"<<v1.size()<<endl;

    //定义一个迭代器
    vector<int>::iterator it;

    int i=0;
    //记录开辟新空间次数
    int count=0;

    for (i;i<100;i++) {
        //尾插法
        v1.push_back(i);

        if(it != v1.begin()){
            count++;
            cout<<"第"<<count<<"次开辟的空间容量"<<v1.capacity()<<endl;

            //重新指向新空间
            it=v1.begin();
        }
    }

    //遍历一下
    for (;it!=v1.end();it++) {
        cout<<*it<<" ";
    }
    cout<<endl;
}
int main()
{

    test01();
    return 0;
}

2、vector容器的 初始化,交换,空,定义容器空间长度的一些函数

#include <iostream>
#include <vector>
using namespace std;
//定义一个遍历函数,方便下面调用
void itPrint(vector<int> v){

    vector<int>::iterator it=v.begin();

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

}
//vector容器的初始化,交换,空,定义容器空间长度的一些函数

void implItPrint(){
    vector<int> v2(5,100);//初始化int型的5个100在容器中
    itPrint(v2);

    vector<int> v3(v2.begin(),v2.end());//将v2拷贝到v3

    vector<int> v4;
    //v4=v2;//一样的效果,直接赋值
    //v4.assign(v2.begin(),v2.end());
    v4.assign(10,100);//初始化10个100

    v4.swap(v3);//交换函数
    itPrint(v3);
    itPrint(v4);

    cout<<v4.size()<<endl;//输出v4中元素的个数

    //v4.empty(),为空才正确
    if(v4.empty()){
        cout<<"v4确实为空"<<endl;
    }else {
        cout<<"v4不为空"<<endl;
    }

    vector<int> v5;
    v5.assign(5,30);

    v5.resize(10);//重新指定容器长度,为10,并且多出部分,补0,超出部分删除。
    //v5.resize(10,2);//超出部分补2
    v5.reserve(20);//预留容器长度为20个元素,多出的部分未被初始化,也不可访问
}
int main()
{
    implItPrint();
    return 0;
}

3、vector容器的插入,查询,修改,删除

#include <iostream>
#include <vector>
using namespace std;
//vector容器的插入,查询,修改,删除
void testCRUD(){
    vector<int>  v6;
    v6.push_back(10);
    v6.push_back(20);
    v6.push_back(30);
    v6.push_back(40);
    v6.push_back(50);
    cout<<"v6的头元素:"<<v6.front()<<"  v6的尾元素"<<v6.back()<<endl;

    //用[]越界不报错,用at越界会抛异常
    cout<<v6[2]<<"  "<<v6.at(2)<<endl;
    v6.at(2)=300;
    v6[3]=400;

    v6.pop_back();//删除尾部元素
    itPrint(v6);//10 20 300 400

    v6.insert(v6.begin()+1,3,20);//在迭代器指向位置+1处,插入3个20
    itPrint(v6);//10 20 20 20 20 300 400

    v6.erase(v6.begin()+1,v6.begin()+4);//删除迭代器指向位置+1(不包括)到4(包括)处
    itPrint(v6);//10 20 300 400

    v6.clear();//清空容器

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

4、巧用swap函数收缩内存空间

#include <iostream>
#include <vector>
using namespace std;
//巧用swap函数收缩内存空间
void swapShrink(){
    vector<int>  v7;
    v7.reserve(100);

    v7.push_back(10);
    v7.push_back(20);
    v7.push_back(30);
    v7.push_back(40);
    v7.push_back(50);
    cout<<"v7的容量:"<<v7.capacity()<<"  v7长度:"<<v7.size()<<endl;
    //v7.resize(4);//resize函数只能修改长度大小,不能修改容量

    vector<int> (v7).swap(v7);
    cout<<"v7的容量:"<<v7.capacity()<<"  v7长度:"<<v7.size()<<endl;//4,4
}
int main()
{
    swapShrink();
    return 0;
}

5、vector容器的 嵌套

//vector容器嵌套
void nestVector(){
    vector<int> v8(3,10);
    vector<int> v9(3,100);
    vector<int> v10(3,1000);

    //定义一个总容器,存放容器
    vector<vector <int>> vsum;
    vsum.push_back(v8);
    vsum.push_back(v9);
    vsum.push_back(v10);

//第一种遍历:
//    vector<vector<int>> ::iterator it=vsum.begin();
//    for (;it!=vsum.end();it++) {
//        vector<int> :: iterator mit=(*it).begin();
//        for (;mit!=(*it).end();mit++) {
//             cout<<(*mit)<<"  ";
//        }
//        cout<<endl;
//    }
//第二种遍历:
    for (auto e:vsum ) {
        //用的是前面定义的迭代器
        itPrint(e);
    }
}

6、自定义类的vector容器,以及 sort()排序( 要加上算法头文件)

#include <iostream>
#include <vector>
//算法头文件
#include <algorithm>
using namespace std;
//sort函数进行排序
void sortVector(){
    vector<int> v11;
    v11.push_back(10);
    v11.push_back(30);
    v11.push_back(50);
    v11.push_back(20);
    v11.push_back(40);

    sort(v11.begin(),v11.end());
    itPrint(v11);
}

//自定义类
class Person{
    friend void printPerson(vector<Person> &v);
    friend bool comparePerson(Person &ob1,Person &ob2);
private:
    int num;
    string name;
public:
    Person(){}
    Person(int num,string name){
        this->num=num;
        this->name=name;
    }
};

//自定义遍历函数
void printPerson(vector<Person> &v){
    vector<Person> ::iterator itp=v.begin();
    for (;itp!=v.end();itp++) {
        cout<<(*itp).num<<" "<<(*itp).name<<endl;;
    }
}

//自定义比较规则
bool comparePerson(Person &ob1,Person &ob2){
    return ob1.num<ob2.num;
}

void custom(){
    vector<Person> vp;
    vp.push_back(Person(002,"权总"));
    vp.push_back(Person(003,"轩总"));
    vp.push_back(Person(001,"煤总"));

    //打印vp中的Person
    printPerson(vp);

    //指定排序规则
    sort(vp.begin(),vp.end(),comparePerson);
    printPerson(vp);

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

4、deque容器

1、双向开口的连续线性空间,至少逻辑上看是连续的,两端都可以做插入删除操作,并且deque没有容量的概念,是由一段一段的定量的连续空间构成,如果有必要在deque前端或尾端增加新空间,便添加一段连续定量的空间,串接在deque的头部或尾部。deque是分段连续内存空间,通过中央控制,来维持整体连续的我假象

在这里插入图片描述
在这里插入图片描述

2、deque容器的函数调用(与vector差不多)

#include <iostream>
#include <deque>
using namespace std;

void printDequeInt(deque<int> &d){
    deque<int>::iterator it=d.begin();
    for(;it!=d.end();it++){
        cout<<(*it)<<" ";
    }

//    for (int e:d) {
//        cout<<e<<"  ";
//    }
   cout<<endl;
}

void deque01(){
    deque<int> d1;
    d1.push_back(1);
    d1.push_back(2);
    d1.push_back(3);
    d1.push_front(5);
    d1.push_front(6);
    d1.push_front(7);

    printDequeInt(d1);//765123
    cout<<"d1的长度:"<<d1.size()<<endl;

    //尾删
    d1.pop_back();
    //头删
    d1.pop_front();
    printDequeInt(d1);//6512

    //插入4
    d1.insert(d1.begin()+2,4);
    printDequeInt(d1);//65412
    //插入2个3
    d1.insert(d1.begin()+3,2,3);
    printDequeInt(d1);//6543312
}
int main()
{
    deque01();
    return 0;
}

5、stack容器:栈(先进后出,栈不提供迭代器遍历)

#include <iostream>
#include <stack>
using namespace std;

void testStack(){
    stack<int> s1;
    s1.push(1);
    s1.push(2);
    s1.push(3);

    //输出一下栈内容,注意栈无法使用迭代器遍历
    if(!s1.empty()){
        cout<<"栈的大小为:"<<s1.size()<<endl;
        while(!s1.empty()){

            //top函数:返回栈顶元素
            cout<<s1.top()<<"  ";

            //pop函数,移除栈顶元素
            s1.pop();
        }
        cout<<endl;
    }
}
int main()
{
    testStack();
    return 0;
}

6、queue容器(先进先出,不提供迭代器遍历)

1、queue是先进先出的数据结构,queue允许从一端增加,从一端移除

#include <iostream>
#include <queue>
using namespace std;

void testqueue(){
    queue<int> q1;
    q1.push(1);
    q1.push(2);
    q1.push(3);

    //返回最后进去元素
    cout<<"最后进去的元素为:"<<q1.back()<<endl;

    //输出一下栈内容,注意栈无法使用迭代器遍历
    if(!q1.empty()){
        cout<<"q1的大小为:"<<q1.size()<<endl;
        while(!q1.empty()){

            cout<<q1.front()<<" ";
            //pop函数,移除队头元素
            q1.pop();
        }
        cout<<endl;
    }
}
int main()
{
    testqueue();
    return 0;
}

7、list链表容器

1、list:采用动态存储分配,不会造成内存浪费和溢出,执行插入和删除很方便,修改指针即可

在这里插入图片描述

#include <iostream>
#include <list>
#include <algorithm>
using namespace std;

void printListInt(list<int> &l){

    for (int e:l) {
        cout<<e<<" ";
    }
    cout<<endl;
}

void testlist(){
    list<int> l1;
    l1.push_back(10);
    l1.push_back(20);
    l1.push_front(30);
    l1.push_front(40);

    printListInt(l1);//40,30,10,20


    //list容器的迭代器是双向迭代器,不支持+2(随机访问迭代器支持),但支持 ++
    list<int>::iterator it=l1.begin();
    //it+2,  不支持
    it++;
    it++;
    l1.insert(it,3,100);
    printListInt(l1);//40,30,100,100,100,20

    //  STL提供的算法,只支持随机访问迭代器,而list是双向迭代器,所以STL的sort不支持list
    //sort(l1.begin(),l1.back());//不支持

    l1.sort();
    printListInt(l1);//10,20,30,40,100,100,100


    l1.reverse();//反转链表,颠倒顺序
    printListInt(l1);//100,100,100,40,30,20,10
}
int main()
{
    testlist();
    return 0;
}

8、set/multidset容器

1、set的特性所有元素都会根据元素的键值自动被排序,set元素键值也是实值;set不允许两个元素有相同的键值(不允许重复),multiset是允许重复的,其他两者无其他差别。

2、set以及对组

#include <iostream>
#include <set>
using namespace std;

//第一部分
void printSetInt(set<int> &s){

    //这里用const_iterator只读迭代器,防止修改元素,导致不是有序的容器了
    set<int> :: const_iterator it=s.begin();
    for (;it!=s.end();it++) {
        cout<<(*it)<<" ";
    }
    cout<<endl;
}
void testSet(){
    set<int> s1;
    multiset<int> m1;

    s1.insert(10);
    s1.insert(20);
    s1.insert(40);
    s1.insert(30);
    printSetInt(s1);//10,20,30,40,自动会排序

    s1.insert(20);
    printSetInt(s1);//还是10,20,30,40,不会重复出现

    //输出元素键值为20的个数
    cout<<s1.count(20)<<endl;//1


    set<int>::const_iterator ret;
    ret=s1.lower_bound(20);
    if(ret!=s1.end()){
        cout<<"下线为:"<<*ret<<endl;
    }

    set<int>::const_iterator ret2;
    ret2 =s1.upper_bound(40);
    if(ret2!=s1.end()){
        cout<<"上线为:"<<*ret2<<endl;
    }

    //以对组的方式存储上下限
    pair <set<int>::const_iterator,set<int>::const_iterator> pa;

    pa=s1.equal_range(40);
    if(pa.first!=s1.end()){
        cout<<"下线为:"<<*(pa.first)<<endl;
    }
    if(pa.second!=s1.end()){
        cout<<"上线为:"<<*(pa.second)<<endl;
    }


    //对组
    pair<int,string> p1=make_pair(007,"权总");
    cout<<p1.first<<" "<<p1.second;
}

3、改变排序规则,再插入前通过设置;类型时修改

//第二部分
//改变排序规则,再插入前通过设置;类型时修改
class mygreater{
public:
    bool operator()(int a,int b){
        return a>b;
    }
};

void printSetInt(set<int,mygreater> &s){

    //这里用const_iterator只读迭代器,防止修改元素,导致不是有序的容器了
    set<int,mygreater> :: const_iterator it=s.begin();
    for (;it!=s.end();it++) {
        cout<<(*it)<<" ";
    }
    cout<<endl;
}

void testSet02(){
    //set<int,排序规则> s2;
    set<int,mygreater> s2;

    s2.insert(10);
    s2.insert(20);
    s2.insert(40);
    s2.insert(30);

    printSetInt(s2);//40,30,20,10
}

4、set存放自定义数据必须修改排序规则

//第三部分
//set存放自定义数据必须修改排序规则
class myPerson;
class Person{
    friend class myPerson;
    friend void printSetPerson(set<Person,myPerson> &s);
private:
    int num;
    string name;
public:
    Person(){}
    Person(int num,string name){
        this->num=num;
        this->name=name;
    }
};

class myPerson{
public:
    bool operator()(Person p1,Person p2){
        return p1.num<p2.num;
    }
};

void printSetPerson(set<Person,myPerson> &s){

    set<Person,myPerson> :: const_iterator it=s.begin();
    for (;it!=s.end();it++) {
            cout<<(*it).num<<"  "<<(*it).name<<endl;
    }
    
//    for(Person p:s){
//        cout<<p.num<<"  "<<p.name<<endl;
//    }

    cout<<endl;
}

void testPerson(){
    set<Person,myPerson> s3;
    s3.insert(Person(001,"权总"));
    s3.insert(Person(003,"轩总"));
    s3.insert(Person(002,"煤总"));

    printSetPerson(s3);
}
int main()
{
    testPerson();
    return 0;
}

9、map/multimap容器(红黑树)

1、map元素会根据键值自动排序,map所有元素都是pair,同时拥有键值(第一个元素),和实值(第二个元素),map不允许有重复的键值,multimap允许

#include <iostream>
#include <map>
using namespace std;

class Person{
    friend void printAll(map<int,Person> m);
private:
    int num;
    string name;
public:
    Person(){}
    Person(int num,string name){
        this->num=num;
        this->name=name;
    }
};

void printAll(map<int,Person> m){
    map<int,Person>::const_iterator it=m.begin();
    for (;it!=m.end();it++) {
        cout<<(*it).first<<"  "<<(*it).second.num<<" "<<(*it).second.name<<endl;
    }
}
void testmap(){
    map<int,Person> m1;
    //方式一
    m1.insert(pair<int,Person>(001,Person(001,"轩总")));
    //方式二(推荐)
    m1.insert(make_pair(002,Person(002,"权总")));
    m1.insert(make_pair(003,Person(003,"煤总")));
    m1.insert(make_pair(004,Person(004,"超哥")));

    printAll(m1);
}
int main()
{
    testmap();
    return 0;
}