• C++笔记


    目录

    内存分区

    cpp将内存划分为4个区域

    1. 代码区(程序执行前):存放函数二进制代码
    2. 全局区(程序执行前):存放全局变量、静态变量以及常量
    3. 栈区:由编译器自动分配释放,存放函数参数值,局部变量
    4. 堆区:由人分配和释放(new,delete),程序结束之后系统会回收
    #include
    using namespace std;
    
    // 全局变量
    int g_a = 0;
    int g_b = 1;
    
    // 全局常量
    const int c_g_a = 0;
    
    
    int main()
    {
        // 内存分为四个区
        // 代码区,全局区,栈区,堆区
    
        cout<<"全局变量g_a的地址:"<<&g_a<<endl;
        cout<<"全局变量g_b的地址:"<<&g_b<<endl;
    
        // 局部变量
        int a = 0;
        int b = 1;
        cout<<"局部变量a的地址:"<<&a<<endl;
        cout<<"局部变量b的地址:"<<&b<<endl;
    
        // 静态变量
        static int s_a = 0;
        static int s_b = 1;
        cout<<"静态变量s_a的地址:"<<&s_a<<endl;
        cout<<"静态变量s_b的地址:"<<&s_b<<endl;
    
        //常量
        cout<<"字符串常量 hello world 的地址:"<<&("hello world")<<endl;
        
        cout<<"全局常量c_g_a的地址:"<<&(c_g_a)<<endl;
    
        // 局部常量
        const int c_l_a = 0;
        cout<<"局部常量c_l_a的地址:"<<&(c_l_a)<<endl;
        
        return 0;
    }
    
    
    全局变量g_a的地址:0x407030
    全局变量g_b的地址:0x403010
    局部变量a的地址:0x61fe1c
    局部变量b的地址:0x61fe18
    静态变量s_a的地址:0x407038
    静态变量s_b的地址:0x403014
    字符串常量 hello world 的地址:0x4040d7
    全局常量c_g_a的地址:0x404004
    局部常量c_l_a的地址:0x61fe14
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    观察上述的地址

    栈区

    不要返回局部变量的地址

    #include
    using namespace std;
    
    int* fun()
    {
        int a = 10;
        return &a;
    }
    
    int main()
    {
        int* p = fun();
    
        cout<<*p<<endl;//第一次可以打印正确的数字 10 ,因为编译器做了保留
        cout<<*p<<endl;//第二次这个数据不再保留
    
        return 0;
    
    }
    
    // warning: address of local variable 'a' returned (编译报错)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    堆区

    new / delete

    int* fun()
    {
        int* a = new int(10);//在堆区开辟一个内存,值是10,然后将其的地址赋值给指针p
        return a;
    }
    
    int main()
    {
        int* p = fun();
    
        cout<<*p<<endl;//10
        cout<<*p<<endl;//10
        cout<<*p<<endl;//10
        
        
        // 手动释放
        delete p;
        
        
        
        
            //创建一个堆区的数组
        int* arr = new int[10];
        for(int i=0;i<10;i++)
        {
            arr[i] = i;
        }
    
        for (int i = 0; i < 10; i++)
        {
            cout<<arr[i]<<endl;
        }
    
        // 手动释放
        delete[] arr;
        
    
        return 0;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    引用&

    起别名
    int a = 10;
    int& b = a;
    cout<<"b = "<<b<<endl;//10
    
    b = 20;
    cout<<"a = "<<a<<endl;//20
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 引用必须初始化
    • 引用初始化之后不能改变
    引用做函数参数
    #include
    using namespace std;
    
    
    // 值传递
    void swap01(int a, int b)
    {
        int temp = a;
        a = b;
        b = temp;
    }
    
    
    // 地址传递
    void swap02(int* a,int* b)
    {
        int temp = *a;
        *a = *b;
        *b = temp;
    }
    
    //引用传递,参数理解为起的别名
    void swap03(int& a,int& b)
    {
        int temp = a;
        a = b;
        b = a;
    }
    
    
    int main()
    {
        int a = 10;
        int b = 99;
    
        swap01(a,b);//并没有交换两个值
        swap01(&a,&b);//成功交换
        swap03(a,b);//成功交换
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    引用做函数返回值
    • 函数不能返回局部变量的引用
    //不能返回局部变量的引用
    int& fun01()
    {
        int a = 0;
        return a;
    }
    
    
    int main()
    {
    
        int& ref = fun01();
        cout<<ref<<endl;//运行错误
    
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 函数的调用可以作为左值
    // 函数的调用可以作为左值
    int& fun02()
    {
        static int a = 0;//静态变量,存放在全局区,在程序结束后释放
        return a;
    }
    
    int main()
    {
    
        int& ref2 = fun02();
        cout<<"ref2 = "<<ref2<<endl;//0
        cout<<"ref2 = "<<ref2<<endl;//0
    
        // 函数的调用可以作为左值
        fun02() = 1;
        cout<<"ref2 = "<<ref2<<endl;//1
        cout<<"ref2 = "<<ref2<<endl;//1
        
    
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    引用的本质

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

    int main()
    {
        int a = 10;
        
        //自动转换为 int* const ref = &a; 指针常量是指向一个不可更改的常量,也说明了引用为什么不可更改
        int& ref = a;
        ref = 20;//内部发现是一个引用,自动帮我们转换为 *ref = 20;
        
        cout<<"a = "<<a<<endl;
        cout<<"ref = "<<ref<<endl;
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    常量引用

    常量引用主要用来修饰形参,防止误操作(防止实参被修改)

    //常量引用,用来修饰形参,防止实参被修改
    
    // 这个函数会将外部的实参修改掉
    void printValue01(int& a)
    {
        a = 0;
        cout<<"value + 10 = "<<a + 10<<endl;
    }
    
    // 这个函数保证了在函数内部外部的实参不被修改,如果想修改,编译不通过
    void printValue02(const int& a)
    {
        // a = 0; 编译不通过
        cout<<"value + 10 = "<<a + 10<<endl;
    }
    
    
    int main()
    {
        int b = 99;
        printValue01(a);//会将a修改为0
        
        printValue02(a);//a不会被修改
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    函数重载需要注意的

    引用作为函数参数

    #include
    using namespace std;
    
    
    // 函数重载需要注意的事项
    void fun(int& a)// int& a = 10 不合法
    {
        cout<<"fun(int& a)"<<endl;
    }
    
    void fun(const int& b)// const int& a = 10 合法
    {
        cout<<"fun(const int& b)"<<endl;
    }
    
    
    
    int main()
    {
    
        int a = 10;
        fun(a);//fun(int& a)
        fun(10);//fun(const int& b)
    
    
        return 0;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    函数重载遇到默认参数

    // 函数重载遇到默认参数
    void fun01(int a,int b = 10)
    {
        cout<<"fun01(int a,int b = 10)"<<endl;
    }
    void fun01(int a)
    {
        cout<<"fun01(int a)"<<endl;
    }
    
    int main()
    {
        fun01(0)//编译不通过
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    类和对象

    #include
    
    class Student
    {
    public:
        string name;
        int stuId;
    
        void printInfo()
        {
            cout<<"name = "<<name<<", stuId = "<<stuId<<endl;
        }
    
    protected:
        string address;
        
    private:
        string password;
        
    public:
        void set_address_password(string address,string password)
        {
            address = address;
            password = password;
        }
    
        void get_address_password()
        {
            cout<<"address "<<address<<", password"<<password<<endl;
        }
    
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • public:类内可以访问,类外也可以访问
    • protected:类内可以访问,类外不可以访问,可以访问继承的父类
    • private:类内可以访问,类外不可以访问,不可以访问继承的父类

    struct和class

    cpp中,struct和class的唯一区别就是访问权限不同

    • class 默认权限是 私有private
    • struct 默认权限是 公共public

    构造函数和析构函数

    构造函数和析构函数都是必须有的实现,如果不提供,编译器会提供至少3个函数:默认构造函数(空实现)、析构函数(空实现)、拷贝构造函数 (值拷贝)

    如果用户定义了有参构造函数,cpp不会提供无参构造函数,但是会提供拷贝构造函数

    如果用户定义了拷贝构造函数,cpp不会提供其它构造函数

    1. 构造函数

      可以重载

      创建对象时调用

    2. 析构函数

      不可以重载

      程序的生命周期终结时调用

    #include
    using namespace std;
    
    
    class Person
    {
    public:
        Person()
        {
            cout<<"Person 构造函数"<<endl;
        }
        ~Person()
        {
            cout<<"Person 析构函数"<<endl;
        }
    };
    
    
    void test01()
    {
        Person p;
    }
    
    
    int main()
    {
    
        test01();//该行执行完,构造和析构都会执行(理解对象的生命周期)
    
        Person p;//该行执行完,构造会执行
        cin.get();
    
    
        return 0;
    }
    
    
    Person 构造函数
    Person 析构函数
    Person 构造函数
    1
    Person 析构函数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    构造函数

    3种构造的方法

    class Person
    {
    private:
        int age;
    
    public:
        Person()
        {
            cout<<"Person 无参函数"<<endl;
        }
        Person(int age)
        {
            this->age = age;
            cout<<"Person 有参构造"<<endl;
        }
        Person(const Person& p)
        {
            // 将传入的p的所有属性 拷贝到自身
            this->age = p.age;
            cout<<"Person 拷贝构造函数"<<endl;
        }
    
    
    
        ~Person()
        {
            cout<<"Person 析构函数"<<endl;
        }
    };
    
    
    void test01()
    {
        // 1 
        Person p;//默认构造
        Person p1(10);//有参构造
        Person p2(p1);//拷贝构造
    
        // 默认构造函数 不要加()
        //Person p4();//这种写法并不会创建一个对象,编译器会认为这是一个函数声明
    
        //2 显示法
        Person p5;
        Person p6 = Person(20);
        Person p7 = Person(p5);
    
        // 匿名对象
        // Person(30);//当前执行结束后,系统会立即回收
        
        //注意: 不要用拷贝构造函数 来创建匿名对象
        //Person(p3);
    
    
        // 3 隐式转换
        Person p8 = 10;
        Person p9 = p8;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    Person fun()
    {
        Person p1;
        return p1;//这里返回的并不是p1对象,而是执行力拷贝构造函数,创建了一个新的对象
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    深拷贝和浅拷贝

    浅拷贝:简单的赋值拷贝操作

    深拷贝:在堆区重新申请空间,进行拷贝操作

    如果有属性是在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题

    class Car
    {
    private:
        int car_id;
        double* car_value;
    
    public:
        Car()
        {
            cout<<"无参构造函数";
        }
        Car(int id,double value)
        {
            car_id = id;
            car_value = new double(value);//在堆区new一个空间,返回一个指针
            cout<<"有参构造函数";
        }
    
        //自己实现拷贝构造函数,解决编译器提供的拷贝构造函数带来的浅拷贝问题
        Car(const Car& car)
        {
            car_id = car.car_id;
            //car_value = car.car_value;//编译器提供的拷贝构造函数,就是这行代码,浅拷贝
            // 深拷贝,解决car_value内存释放冲突问题
            car_value = new double(*car.car_value);
        }
    
        ~Car()
        {
            if (car_value != NULL)//car_value是在堆区手动创建的,需要手动释放
            {
                delete car_value;
                car_value = NULL;
            }
            cout<<"析构函数"<<endl;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    初始化列表

    class A
    {
    private:
        int A_a;
        int A_b;
        int A_c;
    
    public:
        A(int a,int b,int c):A_a(a),A_b(b),A_c(c){}
        
    };
    
    
    int main()
    {
        A aa(1,3,4);
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    A类对象作为B类的属性

    class Phone
    {
        string name;
    }
    
    class Person
    {
        int id;
        Phone phone;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 构造时先构建A的对象,再构造B对象自身
    • 析构函数相反

    静态成员

    1. 所有对象都共享同一份数据或函数
    2. 编译阶段就分配内存
    3. 类内声明,类外初始化
    4. 静态成员函数只能访问静态的成员变量
    class A
    {
     private:
        static string name;//类外访问不到
     public:
        static int id;
        
        static void fun()
        {
            cout<<id<<endl;//只能访问静态成员变量
            cout<<"静态成员函数fun";
        }
    };
    
    string A::name = "zhangsan";
    int A::id = 0;
    
    int main()
    {
        Person p1 = Person();
        
        //访问方式1
        p1.name;
        
        //访问方式2
        Person::name;
           
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

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

    空对象占用的内存空间为 1

    编译器会给每个空对象分配 1 个字节空间,为了区分空对象占内存的地址,每个空对象也应该有一个独一无二的内存地址

    class A
    {
        
    };
    
    int main()
    {
        A a;//空对象 sizeof(a) = 1
    }
    
    
    
    class B
    {
        int aa;//属于类的对象
        static int bb;//不属于类的对象
        void fun();//不属于类的对象
    };
    
    int main()
    {
        B b;//sizeof(b) = 4
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    this指针

    • 解决命名冲突
    • 返回对象本身 *this
    class Person
    {
        int age;
        Person(int age)
        {
            this->age = age;//this指针解决命名冲突
        }
        
        Person& addAge01(Person& p)//返回 引用
        {
            this->age += p.age;
            return *this;//返回对象本体
        }
        
        Person addAge02(Person& p)//返回 值
        {
            this->age += p.age;
            return *this;//返回一个新的对象(调用拷贝构造函数)
        }
        
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    空指针访问成员

    class Phone
    {
        int age;
        void showClass()
        {
            cout<"this is class Phone";
        }
        void showAge()
        {
            if (this == NULL)
            {
                return;
            }
            cout<<"age is "<<age<<;//此处的age其实是this->age
        }
    }
    
    int main()
    {
        Phone* p = NULL;
        phone.showClass();//正常执行
        phone.showAge();//报错 空指针异常
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    const修饰成员函数

    常函数

    • 常函数不可以修改成员属性
    • 成员属性声明时加 mutable,在常函数中就可以修改
    class A
    {
        //this指针本质是一个指针常量,指针的指向是不可修改的
        //后面加const,意味着指针指向的值也不可以修改
        void showA() const
        {
            //this->age = 10;//会报错,this指针不可以修改
            this->id = 1;//正常执行      
        }
        int age;
        mutable id;//特殊变量,在常函数和常对象中可以修改
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    常对象

    • 声明对象前加 const

    • 常对象只能调用常函数

    void fun()
    {
        const Person p;//在对象前加const,变为常对象
        //p.age = 10;//错误,不能被修改
        p.id = 1;//正确,可以修改
        
        p.showA();//正确,可以执行 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    友元

    全局函数作友元,可以访问类的私有成员

     #include
     #include
     using namespace std;
    
    
    class Home
    {
        //声明全局函数friend_fun是一个友元,就可以访问Home中的的私有成员
        friend void friend_fun(Home* home);
    
    public:
    
        Home()
        {
            this->sitting_room = "客厅";
            this->bad_room = "卧室";
        }
    
        string sitting_room;
    
    
    private:
        string bad_room;
    };
    
    void friend_fun(Home* home)
    {
        cout<<"公共属性 "<<home->sitting_room<<endl;
        cout<<"私有属性 "<<home->bad_room<<endl;
    }
    
    
    
     int main()
     {
        Home h1;
        friend_fun(&h1);
        return 0;
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    作友元

    class TV
    {
    public:
        Home* home;
        
        TV()
        {
            this->home = new Home;
        }
    }
    
    class Home
    {
    	//类作友元,
    	friend class TV;
    
    public:
        Home()
        {
            this->sitting_room = "客厅";
            this->bad_room = "卧室";
        }
        string sitting_room;
    
    private:
        string bad_room;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    成员函数作友元

    class TV
    {
    private:
            Home* home;
    
    public:   
        void visit01()
        {
            cout<<home->sitting_room<<endl;
            cout<<home->bad_room<<endl;
        }
        
        void visit02()
        {
            cout<<home->sitting_room<<endl;
            //cout<bad_room<
        }
    }
    
    class Home
    {
        //声明另一个类的成员函数作为友元
        friend void TV::visit01();
    
    public:
        Home()
        {
            this->sitting_room = "客厅";
            this->bad_room = "卧室";
        }
        string sitting_room;
    
    private:
        string bad_room;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    运算符重载

    +运算 符重载,实现两个自定义对象的相加
    • 成员函数重载
    • 全局函数重载
    #include
    using namespace std;
    
    class Person
    {
    public:
        Person(){}
    
        Person(int a,int b)
        {
            this->m_a = a;
            this->m_b = b;
        }
    
        // 1 成员函数重载 + 运算符
        Person operator+(Person& p)
        {
            Person temp;
            temp.m_a = this->m_a + p.m_a;
            temp.m_b = this->m_b + p.m_b;
            return temp;
        }
    
        int m_a; 
        int m_b;
    };
    
    
    
    // 2 全局函数重载 + 运算符
    Person operator+(Person& p1,Person& p2)
    {
        Person p3;
        p3.m_a = p1.m_a + p2.m_a;
        p3.m_b = p1.m_b + p2.m_b;
        
        return p3;
    }
    
    //函数重载的版本
    Person operator+(Person& p,int num)
    {
        Person p3;
        p3.m_a = p.m_a + num;
        p3.m_b = p.m_b + num;
        
        return p3;
    }
    
    
    
    int main()
    {
    //实现两个自定义数据类型相加的运算
        Person p1 = Person(2,3);
        Person p2 = Person(10,20);
    
        Person p3 = p1 + p2;
        
        Person p4 = p1 + 99;
    
        cout<<"p3.m_a = "<<p3.m_a<<endl;//12
        cout<<"p3.m_b = "<<p3.m_b<<endl;//23
        
        cout<<"p4.m_a = "<<p4.m_a<<endl;//101
        cout<<"p4.m_b = "<<p4.m_b<<endl;//102
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    <<运算符重载
    实现输出一个自定义对象的各属性
    class Person
    {
        friend ostream& operator<<(ostream& out,Person p);
    
    public:
        Person(){}
    
        Person(int a,int b)
        {
            this->m_a = a;
            this->m_b = b;
        }
    
    private:
        int m_a; 
        int m_b;
    };
    
    
    //只能用全局函数实现 重载<<运算符
    ostream& operator<<(ostream& out,Person p)
    {
        out<<"m_a = "<<p.m_a<<" m_b = "<<p.m_b;//需要将这个函数声明为友元,访问私有属性
        return out;
    }
    
    
    int main()
    {
        Person p(3,5);
        cout<<p<<endl;
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    ++运算符重载
    #include
    using namespace std;
    
    class MyInteger
    {
        friend ostream& operator<<(ostream& out,MyInteger myint);
    
    
    public:
        MyInteger(){}
    
        MyInteger(int m)
        {
            this->m_num = m;
        }
    
    
        //重载 前置++ 运算符
        MyInteger& operator++()//用MyInteger& 是为了对自身进行返回,一直对一个数据进行++
        //如果 用MyInteger,则会新创建一个MyInteger对象
        {
            //先进行++运算
            this->m_num++;
            // 在将自身返回
            return *this;
        }
    
    
        //重载 后置++ 运算符
        MyInteger operator++(int)//int表示站位参数,可以用于区分前置和后置
        {
            // 先记录最开始的值
            MyInteger temp = *this;
            // 再递增
            this->m_num++;
            // 返回最开始的值
            return temp;//局部对象,只能返回值,不能返回引用
        }
    private:
        int m_num;
    };
    
    
    // 重载<<运算符
        ostream& operator<<(ostream& out,MyInteger myint)
        {
            out<<myint.m_num;
            return out;
        }
    
    
    int main()
    {
        MyInteger myint(0);
        cout<<++(++myint)<<endl;//2
    
        MyInteger myint02(10);
        cout<<(myint02++)<<endl;//10
        cout<<myint02<<endl;//11
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    =运算符重载
    #include
    using namespace std;
    
    class Person
    {
    
    public:
        int* m_age;
    
        Person(){}
    
        Person(int age)
        {
            this->m_age = new int(age);
        }
    
        ~Person()
        {
            if (this->m_age != NULL)
            {
                delete this->m_age;
                this->m_age = NULL;
            }
        }
    
    
        Person& operator=(Person& p)
        {
            //编译器提供浅拷贝,会出现内存释放冲突
            //this->m_age = p.m_age;
    
            //1 应该先判断是否有属性在堆区,如果有,先释放干净,然后再深拷贝
            if (this->m_age != NULL)
            {
                delete this->m_age;
                this->m_age = NULL;
            }
    
            //深拷贝
            this->m_age = new int(*p.m_age);
    
            return *this;
        }
    };
    
    
    int main()
    {
    
        Person p1(20);
    
        Person p2;
        p2 = p1;
        cout<<"p2 = "<<*p2.m_age<<endl;
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    类对象=的重载
    #include
    #include
    using namespace std;
    
    
    class Person
    {
    public:
        Person(int age,string name)
        {
            this->m_age = age;
            this->m_name = name;
        }
    
        //重载两个对象的关系运算符
        bool operator==(Person& p)
        {
            if (this->m_age == p.m_age && this->m_name==p.m_name)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    
    
        bool operator!=(Person& p)
        {
            if (this->m_age == p.m_age && this->m_name==p.m_name)
            {
                return false;
            }
            else
            {
                return true;
            }
        }
    
        int m_age;
        string m_name;
    };
    
    
    int main()
    {
        Person p1(20,"zhangsan");
        Person p2(20,"zhangsan");
    
        if (p1 == p2)
        {
            cout<<"p1和p2 相等"<<endl;
        }
        else
        {
            cout<<"p1和p2 不相等"<<endl;
        }
    
    
        if (p1 != p2)
        {
            cout<<"p1和p2 不相等"<<endl;
        }
        else
        {
            cout<<"p1和p2 相等"<<endl;
        }
    
    
        cin.get();
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    重载函数调用()运算符——仿函数
    #include
    #include
    using namespace std;
    
    class printClass
    {
    public:
        // 重载函数调用()
        void operator()(string s)
        {
            cout<<s<<endl;
        }
    };
    
    
    class addClass
    {
    public:
        int operator()(int a,int b)
        {
            return a+b;
        }
    };
    
    
    int main()
    {
        printClass p;
        p("hello cpp!");
    
        addClass a;
        int res = a(3,7);
        cout<<res<<endl;
    
        //匿名函数
        cout<<addClass()(1,9)<<endl; 
    
    
        cin.get();
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    继承

    降低代码重复

    父类:基类

    子类:派生类

    #include 
    using namespace std;
    
    class Page
    {
    public:
        void header()
        {
            cout << "页面头部内容。。" << endl;
        }
        void footer()
        {
            cout << "页面尾部内容。。" << endl;
        }
    };
    
    // 继承
    class BuyPage : public Page
    {
    public:
        void content()
        {
            cout << "购买页面" << endl;
        }
    };
    
    class Browse : public Page
    {
    public:
        void content()
        {
            cout << "浏览页面" << endl;
        }
    };
    
    int main()
    {
        BuyPage buy;
        buy.header();
        buy.footer();
        buy.content();
    
        Browse browse;
        browse.header();
        browse.footer();
        browse.content();
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    继承方式

    在这里插入图片描述

    #include 
    using namespace std;
    
    class Father
    {
    public:
        int m_a = 1;
    
    protected:
        int m_b = 2;
    
    private:
        int m_c = 3;
    };
    
    class Son01 : public Father
    {
    public:
        void test01()
        {
            cout << m_a << endl; // 公共
            cout << m_b << endl; // 保护
            // cout<
        }
    };
    
    class Son02 : protected Father
    {
    public:
        void test02()
        {
            cout << m_a << endl; // 变为保护
            cout << m_b << endl; // 变为保护
            // cout<
        }
    };
    
    class Son03 : private Father
    {
    public:
        void test03()
        {
            cout << m_a << endl; // 变为私有
            cout << m_b << endl; // 变为私有
            //cout<
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    继承中对象属性大小

    父类中的所有非静态成员属性都会被子类继承下去,只是某些属性的权限不同

    class Father
    {
    public:
        int m_a = 1;
    
    protected:
        int m_b = 2;
    
    private:
        int m_c = 3;
    };
    
    class Son04 : public Father
    {
    public:
        int m_d = 0;
    };
    
    void test()
    {
        //私有成员也被继承了,只是访问不到而已
        cout << "size of : "<<sizeof(Son04) << endl;//16
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    继承中的构造和析构顺序
    • 先构造父类,再构造子类
    • 析构顺序相反
    class Base
    {
        Base()
        {
            cout<<"Base 构造函数。。"<<endl;
        }
        ~Base()
        {
            cout<<"Base 析构函数。。"<<endl;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    继承同名成员函数
    • 访问子类同名成员函数,直接访问即可
    • 访问父类同名成员函数,需要加作用域
    • 如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有的同名成员函数(包括重载的),需要加作用域
    class Base
    {
        Base()
        {
            int m_a = 100;
        }
        
    public:
        print_hh()
        {
            cout<<"Base: hh"<<endl;
        }
        print_hh(int a)
        {
            cout<<"Base:hh and "<<a<<endl;
        }
    }
    
    class Son:public Base
    {
        Son()
        {
            int m_a = 200;
        }
        
    public:
        print_hh()
        {
            cout<<"Son: hh"<<endl;
        }
    }
    
    test()
    {
    	Son s;
        cout<<"Son: m_a"<<s.m_a<<endl;//200
        cout<<"Base: m_a"<<s.Base::m_a<<endl;//100 父类下的,需要加作用域
        
        s.print_hh();//Son: hh
        s.Base::print_hh();//Base: hh
        
        //如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有的同名成员函数(包括重载的)
        s.Base::print_hh(1);
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    继承同名静态成员

    子类,直接访问

    父类,加作用域

    class Base
    {
    public:
        static int s_a;
        static void fun()
        {
            cout << "Base--fun()" << endl;
        }
    };
    int Base::s_a = 10;
    
    class Son : public Base
    {
    public:
        static int s_a;
        static void fun()
        {
            cout << "Son--fun()" << endl;
        }
    };
    int Son::s_a = 20;
    
    void test01()
    {
        Son s;
        // 通过对象访问
        cout << s.s_a << endl;       // 20
        cout << s.Base::s_a << endl; // 10
        s.fun();                     // Son--fun()
        s.Base::fun();               // Base--fun()
    
        // 通过类名访问
        cout << Son::s_a << endl;       // 20
        cout << Son::Base::s_a << endl; // 10
        Son::fun();
        Son::Base::fun();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    多继承

    class Base1
    {
    public:
        Base1()
        {
            m_a = 1;
        }
        int m_a;
    };
    
    class Base2
    {
    public:
        Base2()
        {
            m_a = 2;
        }
        int m_a;
    };
    
    class Son : public Base1, public Base2
    {
    public:
        Son()
        {
            m_b = 3;
            m_c = 4;
        }
        int m_b;
        int m_c;
    };
    
    void test()
    {
        Son s;
        cout << "size of : " << sizeof(s) << endl; // 16
    
        // 不建议用多继承,因为会出现二义性,必须加作用域用于区分
        cout << "Base1 :" << s.Base1::m_a << endl; // 1
        cout << "Base2 :" << s.Base2::m_a << endl; // 2
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    菱形继承及解决方法(虚继承)

    两个派生类继承同一个基类,又有某个类同时继承两个派生类

    	 A
       /   \
       B   C
       \   /
         D
    
    • 1
    • 2
    • 3
    • 4
    • 5
    #include 
    using namespace std;
    
    class Animal
    {
    public:
        Animal()
        {
            m_age = 0;
        }
        int m_age;
    };
    
    // 虚继承
    class Sheep : virtual public Animal
    {
    public:
        Sheep()
        {
            m_age = 10;
        }
    };
    
    // 虚继承
    class Tuo : virtual public Animal
    {
    public:
        Tuo()
        {
            m_age = 20;
        }
    };
    
    class SheepTuo : public Sheep, public Tuo
    {
    };
    
    int main()
    {
        SheepTuo st;
        // cout<
        // 作用域区分
        cout << "Sheep : " << st.Sheep::m_age << endl; // 10
        cout << "Tuo : " << st.Tuo::m_age << endl;     // 20
    
        // 如果我们想明确m_age的继承,只需要继承一个m_age,在内存中只有一份,并且减少内存使用,需要使用虚继承
        cout << st.m_age << endl; // 20
    
        // m_age在内存中只有一份,Sheep父类和Tuo父类共享内存,指针指向同一个地址
        st.Sheep::m_age = 50;
        cout << st.m_age << endl; // 50
        st.Tuo::m_age = 70;
        cout << st.m_age << endl; // 70
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    多态

    虚函数,地址晚绑定,在运行阶段绑定函数地址,如果要实现多态,就需要使用虚函数virtual,如果不加virtual,地址早绑定,在编译阶段确定函数地址

    动态多态满足条件

    • 有继承关系
    • 子类重写父类的虚函数
    #include 
    using namespace std;
    
    class Animal
    {
    public:
        //虚函数,地址晚绑定,在运行阶段绑定函数地址,如果要实现多态,就需要使用虚函数virtual
        //如果不加virtual,地址早绑定,在编译阶段确定函数地址
        virtual void speak()
        {
            cout << "animal speaking" << endl;
        }
    };
    
    class Cat : public Animal
    {
    public:
        void speak()
        {
            cout << "cat speaking" << endl;
        }
    };
    
    class Dog : public Animal
    
    {
    public:
        void speak()
        {
            cout << "dog speaking" << endl;
        }
    };
    
    void dospeaking(Animal &animal)
    {
        animal.speak();
    }
    
    int main()
    {
        Cat cat;
        // dospeaking(cat); // animal speaking(Animal类中的speak()不加virtual)
        dospeaking(cat); // cat speaking
    
        Dog dog;
        dospeaking(dog); // dog speaking
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    开闭原则
    • 对扩展进行开放
    • 对修改进行关闭
    多态实现计算器
    • 可读性好
    • 维护性高
    #include 
    using namespace std;
    
    class AbstractCalcuator
    {
    public:
        virtual int getResult()
        {
            return 0;
        }
        int number01;
        int number02;
    };
    
    class AddCalcuator : public AbstractCalcuator
    {
    public:
        int getResult()
        {
            return number01 + number02;
        }
    };
    
    class SubCalcuator : public AbstractCalcuator
    {
    public:
        int getResult()
        {
            return number01 - number02;
        }
    };
    
    class MulCalcuator : public AbstractCalcuator
    {
    public:
        int getResult()
        {
            return number01 * number02;
        }
    };
    
    int main()
    {
        // 实现多态的条件
        // 父类指针或引用指向子类对象
        AbstractCalcuator *cal = new AddCalcuator; // 在堆区创建对象
        cal->number01 = 10;
        cal->number02 = 20;
        cout << "number01 + number02 = " << cal->getResult() << endl;
        // cal指向堆区对象,用完要释放
        delete cal;
    
        cal = new SubCalcuator; // 在堆区创建对象
        cal->number01 = 10;
        cal->number02 = 20;
        cout << "number01 - number02 = " << cal->getResult() << endl;
        // cal指向堆区对象,用完要释放
        delete cal;
    
        cal = new MulCalcuator; // 在堆区创建对象
        cal->number01 = 10;
        cal->number02 = 20;
        cout << "number01 * number02 = " << cal->getResult() << endl;
        // cal指向堆区对象,用完要释放
        delete cal;
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    纯虚函数和抽象类

    • 抽象类无法实例化对象

    • 子类必须重写纯虚函数,负责也属于抽象类

    #include 
    using namespace std;
    
    class Base
    {
    public:
        virtual void fun() = 0;
    };
    
    class Son : public Base
    {
    public:
        void fun()
        {
            cout << "fun()函数正在调用。。" << endl;
        }
    };
    
    int main()
    {
        Base *base = new Son;
        base->fun();
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    虚析构和纯虚析构

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

    解决:将父类中的析构函数改为虚析构或者纯虚析构

    虚析构或纯虚析构是用来解决父类指针释放子类对象

    如果子类没有堆区数据,可以不写虚析构或纯虚析构

    拥有纯虚析构函数的类也属于抽象类

    #include 
    #include 
    using namespace std;
    
    class Animal
    {
    public:
        Animal()
        {
            cout << "Animal构造函数" << endl;
        }
        // 利用虚析构,可以解决 父类指针释放子类对象时不干净的问题
        virtual ~Animal()
        {
            cout << "Animal析构函数" << endl;
        }
        virtual void eat() // 多态的实现条件
        {
            cout << "animal eating..." << endl;
        }
    };
    
    class Cat : public Animal
    {
    public:
        Cat(string name)
        {
            m_name = new string(name); // 堆区创建一个变量
            cout << "Cat构造函数" << endl;
        }
        ~Cat()
        {
            if (m_name != NULL)
            {
                delete m_name;
                m_name = NULL;
                cout << "Cat析构函数" << endl;
            }
        }
        string *m_name; // 一个指针类型的属性
    
        void eat()
        {
            cout << *m_name << " eating..." << endl;
        }
    };
    
    int main()
    {
    
        Animal *animal = new Cat("Tom");
        animal->eat();
        // 父类指针在析构时,不会调用子类的析构函数,导致如果子类有堆区属性,没有成功释放,出现内存泄露
        delete animal;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    纯虚析构

    也是一个抽象类,无法实例化对象

    #include 
    #include 
    using namespace std;
    
    class Animal
    {
    public:
        Animal()
        {
            cout << "Animal构造函数" << endl;
        }
        // 纯虚析构,类外必须实现
        virtual ~Animal() = 0;
        virtual void eat() // 多态的实现条件
        {
            cout << "animal eating..." << endl;
        }
    };
     
    Animal::~Animal()
    {
        cout<<"Animal析构函数"<<endl;
    }
    
    class Cat : public Animal
    {
    public:
        Cat(string name)
        {
            m_name = new string(name); // 堆区创建一个变量
            cout << "Cat构造函数" << endl;
        }
        ~Cat()
        {
            if (m_name != NULL)
            {
                delete m_name;
                m_name = NULL;
                cout << "Cat析构函数" << endl;
            }
        }
        string *m_name; // 一个指针类型的属性
    
        void eat()
        {
            cout << *m_name << " eating..." << endl;
        }
    };
    
    int main()
    {
    
        Animal *animal = new Cat("Tom");
        animal->eat();
        // 父类指针在析构时,不会调用子类的析构函数,导致如果子类有堆区属性,没有成功释放,出现内存泄露
        delete animal;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    组装案例
    #include 
    using namespace std;
    
    class CPU
    {
    public:
        virtual void calculate() = 0;
    };
    
    class Mermory
    {
    public:
        virtual void storage() = 0;
    };
    
    class VideoCard
    {
    public:
        virtual void display() = 0;
    };
    
    class Computer
    {
    public:
        Computer(CPU *cpu, Mermory *mermory, VideoCard *videocard)
        {
            m_cpu = cpu;
            m_mermory = mermory;
            m_videocard = videocard;
        }
    
        void work()
        {
            m_cpu->calculate();
            m_mermory->storage();
            m_videocard->display();
        }
    
        ~Computer()
        {
            if (m_cpu == NULL)
            {
                delete m_cpu;
                m_cpu = NULL;
            }
            if (m_mermory == NULL)
            {
                delete m_mermory;
                m_mermory = NULL;
            }
            if (m_videocard == NULL)
            {
                delete m_videocard;
                m_videocard = NULL;
            }
        }
    
    private:
        CPU *m_cpu;
        Mermory *m_mermory;
        VideoCard *m_videocard;
    };
    
    class IntelCPU : public CPU
    {
    public:
        void calculate()
        {
            cout << "Intel cpu working.." << endl;
        }
    };
    class LenovoCPU : public CPU
    {
    public:
        void calculate()
        {
            cout << "Lenovo cpu working.." << endl;
        }
    };
    class IntelMermory : public Mermory
    {
    public:
        void storage()
        {
            cout << "Intel mermory working.." << endl;
        }
    };
    class LenoveMermory : public Mermory
    {
    public:
        void storage()
        {
            cout << "Lenovo mermory working.." << endl;
        }
    };
    class IntelVideoCrad : public VideoCard
    {
    public:
        void display()
        {
            cout << "Intel videocard working.." << endl;
        }
    };
    class LenovoVideoCrad : public VideoCard
    {
    public:
        void display()
        {
            cout << "Lenovo videocard working.." << endl;
        }
    };
    
    int main()
    {
        // 组装一台电脑
        CPU *cpu01 = new IntelCPU;
        Mermory *mermory01 = new IntelMermory;
        VideoCard *videocard01 = new IntelVideoCrad;
    
        Computer *computer01 = new Computer(cpu01, mermory01, videocard01);
        computer01->work();
        delete computer01;
    
        Computer *computer02 = new Computer(new LenovoCPU, new LenoveMermory, new LenovoVideoCrad);
        computer02->work();
        delete computer02;
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129

    文件操作

    • 文本文件:以文本的ASCII码形式存储在计算机上
    • 二进制文件:以二进制形式存储在计算机上
    写文件
    #include 
    #include 
    using namespace std;
    
    int main()
    {
        // 创建写文件流对象
        ofstream ofs;
        // 指定打开文件的方式
        ofs.open("test01.txt", ios::out);
        // 写内容
        ofs << "姓名:张三" << endl;
        ofs << "性别:男" << endl;
        ofs << "年龄:100" << endl;
        ofs << "今天是2023年11月8日。。。" << endl;
        // 关闭流对象
        ofs.close();
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    读文件
    #include 
    #include 
    using namespace std;
    
    int main()
    {
        // 创建读文件流对象
        ifstream ifs;
        // 读取文件
        ifs.open("test01.txt", ios::in);
        if (!ifs.is_open())
        {
            cout << "文件打开失败!!" << endl;
        }
    
        // 第一种
        // char buffer[1024] = {0};
        // while (ifs >> buffer)//按行读取
        // {
        //     cout << buffer << endl;
        // }
    
        // 第二种
        // char buffer[1024] = {0};
        // while (ifs.getline(buffer, sizeof(buffer))) // 每次按行读取存储在buffer中,每次最大读取sizeof()
        // {
        //     cout << buffer << endl;
        // }
    
        // 第三种
        // string buffer;
        // while (getline(ifs, buffer))
        // {
        //     cout << buffer << endl;
        // }
    
        // 第四种
        char c;
        while ((c = ifs.get()) != EOF) // 逐个字符读取,EOF是文件末尾的标志
        {
            cout << c;
        }
    
        // 流关闭
        ifs.close();
    
        system("pause");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    二进制写文件
    #include 
    #include 
    using namespace std;
    
    class Person
    {
    public:
        char name[100];
        int age;
    };
    
    int main()
    {
        // 创建流对象
        ofstream ofs("test02.txt", ios::out | ios::binary);
        // 打开文件
        // ofs.open("test02.txt",ios::out|ios::binary);
    
        // 写文件
        Person perosn = {"james", 40};
        ofs.write((const char *)&perosn, sizeof(Person));
    
        ofs.close();
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    二进制读文件
    #include 
    #include 
    using namespace std;
    
    class Person
    {
    public:
        char name[100];
        int age;
    };
    
    int main()
    {
        // 创建读文件对象
        ifstream ifs;
        ifs.open("test02.txt", ios::in | ios::binary);
        if (!ifs.is_open())
        {
            cout << "文件打开时失败!!" << endl;
        }
        // 读文件
        Person person;
        ifs.read((char *)&person, sizeof(Person));
        cout << "name: " << person.name << ", age: " << person.age << endl;
    
        ifs.close();
    
        system("pause");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    模版

    泛型编程

    函数模版,类模版

    函数模版
    • 自动类型推导必须推导出一致的数据类型

    • 模版必须要确定出T的数据类型

    #include 
    using namespace std;
    
    // 模版函数
    template <typename T>
    void mySwap(T &a, T &b)
    {
        T temp = a;
        a = b;
        b = temp;
    }
    
    template <class T>
    void printInfo()
    {
        cout << "it's great!" << endl;
    }
    
    int main()
    {
        int a = 10;
        int b = 20;
    
        // 1 自动类型推导
        mySwap(a, b);
        cout << "a = " << a << endl; // 20
        cout << "b = " << b << endl; // 10
    
        // 2 显式指定类型
        mySwap<int>(a, b);
        cout << "a = " << a << endl; // 10
        cout << "b = " << b << endl; // 20
    
        
        
        // 自动类型推导必须要推导出正确的类型
        char c = 'q';
        // mySwap(a,c);//错误的
    
        // 模版必须要确定出T的类型
        //  printInfo();//错误的
        printInfo<int>(); // 必须显式指定
    
        system("pause");
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    例子

    #include 
    using namespace std;
    
    // 交换两个值的模版函数
    template <typename T>
    void my_swap(T &a, T &b)
    {
        T temp = a;
        a = b;
        b = temp;
    }
    
    // 降序排序的模版函数
    template <typename T>
    void sortArr(T arr[], int len) // 降序
    {
        int i = 0;
        while (i < len)
        {
            int max_idx = i;
            for (int j = i + 1; j < len; j++)
            {
                if (arr[j] > arr[max_idx])
                {
                    max_idx = j;
                }
            }
            if (max_idx != i)
            {
                my_swap(arr[max_idx], arr[i]);
            }
            i++;
        }
    }
    
    //打印不同类型数组的模版函数
    template <typename T>
    void printArr(T arr[], int len)
    {
        int i = 0;
        while (i < len)
        {
            cout << arr[i];
            i++;
        }
        cout << endl;
    }
    
    int main()
    {
        char charArr[] = "efcsab";
        int lens01 = sizeof(charArr) / sizeof(char);
        sortArr(charArr, lens01);
        printArr(charArr, lens01);
    
        int intArr[] = {2, 4, 6, 3, 0, 9, 3, 8};
        int lens02 = sizeof(intArr) / sizeof(int);
        sortArr(intArr, lens02);
        printArr(intArr, lens02);
    
        system("pause");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    普通函数和函数模板的区别
    • 普通函数调用时可以发生自动类型转换
    • 函数模版调用时,如果是自动类型推导,不会发生自动类型转换;如果是显式指定类型方式,可以发生自动类型转换
    #include 
    using namespace std;
    
    int myadd01(int a, int b)
    {
        return a + b;
    }
    
    template <typename T>
    T myadd02(T a, T b)
    {
        return a + b;
    }
    
    int main()
    {
        int a = 1;
        int b = 99;
        char c = 'A';
    
        cout << myadd01(a, b) << endl;
        cout << myadd01(1, c) << endl;
    
        cout << myadd02(a, b) << endl;
        // cout << myadd02(a, c) << endl; // 错误,无法推导出一致的数据类型
        cout << myadd02<int>(a, c) << endl;//显式指定,可以发生自动类型转换
    
        system("pause");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    1. 函数模版和普通函数都可以实现,优先调用普通函数
    2. 可以通过空模板参数列表来强制调用函数模板
    3. 函数模版可以发生重载
    4. 函数模版可以产生更好的匹配,优先调用函数模版
    #include 
    using namespace std;
    
    void myprint(int a, int b)
    {
        cout << "01 普通函数" << endl;
    }
    
    template <typename T>
    void myprint(T a, T b)
    {
        cout << "02 模版函数" << endl;
    }
    
    template <typename T>
    void myprint(T a, T b, T c)
    {
        cout << "03 重载的模版函数" << endl;
    }
    
    int main()
    {
        int a = 1;
        int b = 99;
        char c = 'A';
    
        myprint(a, b);   // 01
        myprint<>(a, b); // 02  通过空模板参数列表
    
        myprint<int>(a, b, c); // 03 重载
    
        myprint('A', 'a'); // 02
    
        system("pause"); // 模版函数是更好的匹配
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    既然使用了模版函数,最好不要再提供普通函数,因为会出现二义性

    模版局限性和自定义

    利用具体化的模版,可以解决自定义类型的通用化

    #include 
    using namespace std;
    
    class Person
    {
    public:
        string name;
        int age;
    };
    
    template <typename T>
    bool isEqual(T &a, T &b)
    {
        if (a == b)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    
    // 利用具体化的Person版本实现,会优先调用
    template <>
    bool isEqual(Person &a, Person &b)
    {
        if (a.name == b.name && a.age == b.age)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    
    int main()
    {
        Person p1 = {"zhangsan", 10};
        Person p2 = {"zhangsan", 10};
    
        cout << isEqual(p1, p2) << endl; // 1 ,会优先调用Person模板函数
    
        system("pause");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    类模板

    #include 
    using namespace std;
    
    template <class nameType, class ageType>
    class Person
    {
    public:
        Person(nameType name, ageType age)
        {
            m_name = name;
            m_age = age;
        }
        nameType m_name;
        ageType m_age;
    };
    
    int main()
    {
        Person<string, int> person = {"悟空", 5};
        cout << person.m_name << endl;
        cout << person.m_age << endl;
    
        system("pause");
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    类模板和函数模板的区别
    1. 类模板没有自动类型推导
    2. 类模板在参数列表中可以有默认参数
    template <class nameType, class ageType=int>
    class Person
    {
    public:
        Person(nameType name, ageType age)
        {
            m_name = name;
            m_age = age;
        }
        nameType m_name;
        ageType m_age;
    };
    
    int main()
    {
        //Person person("八戒", 1000);//错误,不能自动类型推导
        Person<string> person("悟空", 5000);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    类模板中成员函数创建时机
    • 普通类中的成员函数一开始就可以创建
    • 类模板中的成员函数在调用时才创建
    class A
    {
    public:
        void showA()
        {
            cout<<"A.."
        }
    }
    
    template<class T>
    class B
    {
    public:
        T obj;
        //在调用时才创建,因为T不确定
        void fun()
        {
            obj.showA();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    类模板对象做函数参数
    1. 指定传入的参数类型(通常使用这种方法,直接了当)
    2. 参数模版化
    3. 整个类模版化
    #include 
    #include 
    using namespace std;
    
    template <class T1, class T2>
    class Person
    {
    public:
        Person(T1 name, T2 age)
        {
            this->m_name = name;
            this->m_age = age;
        }
        void show()
        {
            cout << "name:" << this->m_name << " age:" << this->m_age << endl;
        }
        T1 m_name;
        T2 m_age;
    };
    
    // 1 指定传入的类型
    void printPerson1(Person<string, int> &p)
    {
        p.show();
    }
    
    // 2 参数模版化
    template <class T1, class T2>
    void printPerson2(Person<T1, T2> &p)
    {
        p.show();
    }
    
    // 3 整个类模板化
    template <class T>
    void printPerson3(T &p)
    {
        p.show();
    }
    
    int main()
    {
        Person<string, int> p("悟空", 200);
        printPerson1(p);
    
        Person<string, int> p2("八戒", 100);
        printPerson1(p2);
    
        Person<string, int> p3("如来", 1000);
        printPerson1(p3);
    
        system("pause");
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    类模板与继承

    如果父类是一个类模版,子类需要指定出父类中T的数据类型

    1. 直接制定
    2. 继续使用类模板
    #include 
    #include 
    using namespace std;
    
    template <class T>
    class Base
    {
    public:
        T name;
    };
    
    // 1 直接指定父类中的T类型
    class Sub : public Base<string>
    {
    };
    
    // 2 类模板声明父类中的类型,才能继承
    template <class T1, class T2>
    class Son : public Base<T1>
    {
    public:
        Son()
        {
            cout << "T1: " << typeid(T1).name() << endl;
            cout << "T2: " << typeid(T2).name() << endl;
        }
        T2 age;
    };
    
    int main()
    {
        Sub s;
        s.name = "zhang";
        cout << "name:" << s.name << endl;
    
        Son<string, int> s2;
    
        system("pause");
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    类模板中成员函数的类外实现
    #include 
    using namespace std;
    
    template <class T1, class T2>
    class Person
    {
    public:
        Person(T1 name, T2 age);
    
        T1 m_name;
        T2 m_age;
    
        void showPerson();
    };
    
    // 构造函数类外实现
    template <class T1, class T2>
    Person<T1, T2>::Person(T1 name, T2 age)
    {
        this->m_name = name;
        this->m_age = age;
    }
    
    // 成员函数的类外实现
    template <class T1, class T2>
    void Person<T1, T2>::showPerson()
    {
        cout << "name: " << this->m_name << " age: " << this->m_age << endl;
    }
    
    int main()
    {
    
        Person<string, int> person("zhangsan", 18);
        person.showPerson();
    
        system("pause");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    类模板分文件编写

    由于类模板中的成员函数创建时机是在调用阶段,在分文件编写时无法编译通过类模版的函数

    解决方法:将类模板声明和实现写在一个文件里,约定俗称,以.hpp结尾的文件

    person.hpp

    #pragma once
    #include 
    using namespace std;
    
    template <class T1, class T2>
    class Person
    {
    public:
        Person(T1 name, T2 age);
    
        T1 m_name;
        T2 m_age;
    
        void showPerson();
    };
    
    // 构造函数类外实现
    template <class T1, class T2>
    Person<T1, T2>::Person(T1 name, T2 age)
    {
        this->m_name = name;
        this->m_age = age;
    }
    
    // 成员函数的类外实现
    template <class T1, class T2>
    void Person<T1, T2>::showPerson()
    {
        cout << "name: " << this->m_name << " age: " << this->m_age << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    test.cpp

    #include 
    using namespace std;
    
    #include "person.hpp"
    
    int main()
    {
    
        Person<string, int> person("zhangsan", 18);
        person.showPerson();
    
        system("pause");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    类模板和友元

    全局函数作为友元,类外实现的话,需要让编译器提前知道全局函数的存在,需要将代码写在最前面

    通常使用类内实现

    #include 
    using namespace std;
    
    template <class T1, class T2>
    class Person;
    
    // 类外实现
    template <class T1, class T2>
    void printPerson02(Person<T1, T2> p)
    {
        cout << "name " << p.m_name << " age " << p.m_age << endl;
    }
    
    template <class T1, class T2>
    class Person
    {
        // 1 全局函数,类内实现
        friend void printPerson01(Person<T1, T2> p)
        {
            cout << "name " << p.m_name << " age: " << p.m_age << endl;
        }
    
        // 2 全局函数,类外实现
        // 类外实现,需要让编译提前知道这个函数的存在
        friend void printPerson02<>(Person<T1, T2> p);
    
    public:
        Person(T1 name, T2 age)
        {
            this->m_age = age;
            this->m_name = name;
        }
    
    private:
        T1 m_name;
        T2 m_age;
    };
    
    int main()
    {
        Person<string, int> p1("zhang", 19);
        printPerson01(p1);
    
        Person<string, int> p2("li", 25);
        printPerson02(p2);
    
        system("pause");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    类模板实例-数组模版

    .hpp

    #pragma once
    #include 
    using namespace std;
    
    template <class T>
    class MyArray
    {
    public:
        MyArray(int capacity)
        {
            this->my_capacity = capacity;
            this->my_size = 0;
            this->my_address = new T[this->my_capacity];
        }
    
        // 重写拷贝构造函数,避免浅拷贝带来的问题
        MyArray(const MyArray &arr)
        {
            this->my_capacity = arr.my_capacity;
            this->my_size = arr.my_size;
            // 这是编译器提供的浅拷贝
            // this->my_address = arr.my_address;
    
            // 深拷贝
            this->my_address = new T[arr.my_capacity];
            for (int i = 0; i < this->my_size; i++)
            {
                this->my_address[i] = arr.my_address[i];
            }
        }
    
        // operator= 防止浅拷贝问题
        MyArray &operator=(const MyArray &arr)
        {
            if (this->my_address != NULL)
            {
                delete[] this->my_address;
                this->my_address = NULL;
                this->my_capacity = 0;
                this->my_size = 0;
            }
            // 深拷贝
            this->my_capacity = arr.my_capacity;
            this->my_size = arr.my_size;
            this->my_address = new T[arr.my_capacity];
            for (int i = 0; i < this->my_size; i++)
            {
                this->my_address[i] = arr.my_address[i];
            }
            return *this;
        }
    
        ~MyArray()
        {
            if (this->my_address != NULL)
            {
                // delete[] 释放数组中全部元素的内存空间
                delete[] this->my_address;
                this->my_address = NULL;
            }
        }
    
        // 尾插法
        void push_back(const T &val)
        {
            // 先判断目前的大小是否等于容量
            if (this->my_size == this->my_capacity)
            {
                return;
            }
            // 尾部插入数据,并修改大小
            this->my_address[this->my_size] = val;
            this->my_size++;
        }
    
        // 尾删法
        void pop()
        {
            if (this->my_size == 0)
            {
                return;
            }
            // 逻辑上的删除
            this->my_size--;
        }
    
        // 通过下标方式访问 []
        T &operator[](int index)
        {
            return this->my_address[index];
        }
    
        // 返回数组大小
        int getSize()
        {
            return this->my_size;
        }
    
        // 返回数组容量
        int getCapacity()
        {
            return this->my_capacity;
        }
    
    private:
        int my_capacity; // 容量
        int my_size;     // 大小
        T *my_address;   // 维护的数组地址
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109

    .cpp

    #include 
    #include 
    using namespace std;
    #include "MyArray.hpp"
    
    class Person
    {
    public:
        Person(){};
        Person(string name, int age)
        {
            this->m_name = name;
            this->m_age = age;
        }
        int m_age;
        string m_name;
    };
    
    void printArr(MyArray<int> &arr)
    {
        for (int i = 0; i < arr.getSize(); i++)
        {
            cout << arr[i] << endl;
        }
    }
    
    void printPersonArr(MyArray<Person> &arr)
    {
        for (int i = 0; i < arr.getSize(); i++)
        {
            cout << "name:" << arr[i].m_name << ",age:" << arr[i].m_age << endl;
        }
    }
    
    int main()
    {
        // 有参构造
        // MyArray arr01(5);
        // // 拷贝构造
        // MyArray arr02(arr01);
        // // =拷贝,先清空,再拷贝
        // MyArray arr03(10);
        // arr03 = arr01;
    
        MyArray<int> arr01(5);
        for (int i = 0; i < 5; i++)
        {
            // 利用尾插法插入数据
            arr01.push_back(i);
        }
        printArr(arr01);
    
        cout << "01容量:" << arr01.getCapacity() << endl;
        cout << "01大小:" << arr01.getSize() << endl;
    
        MyArray<int> arr02(arr01);
        arr02.pop();
        printArr(arr02);
        cout << "02容量:" << arr02.getCapacity() << endl;
        cout << "02大小:" << arr02.getSize() << endl;
    
        cout << "-----------------" << endl;
        MyArray<Person> arr_person(5);
        Person p1("zhangsan", 12);
        Person p2("lisi", 25);
        Person p3("wangwu", 36);
        Person p4("hhh", 47);
        arr_person.push_back(p1);
        arr_person.push_back(p2);
        arr_person.push_back(p3);
        arr_person.push_back(p4);
    
        printPersonArr(arr_person);
        arr_person.pop();
        cout << "person大小:" << arr_person.getSize() << endl;
    
        system("pause");
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80

    STL标准模板库

    Standard Template Library

    六大组件

    1. 容器:各种数据结构,vector,list,set,map
    2. 算法:排序,查找
    3. 迭代器:容器和算法之间的胶合剂
    4. 仿函数
    5. 适配器:用来修饰容器或者仿函数或迭代器的
    6. 空间配置器:负责空间配置与管理

    Vector

    存放内置数据类型
    #include 
    #include 
    #include 
    using namespace std;
    
    void my_print(int val)
    {
        cout << val << endl;
    }
    
    int main()
    {
        vector<int> v;
        v.push_back(10);
        v.push_back(20);
        v.push_back(30);
        v.push_back(40);
    
        // 第一种遍历方式
        // vector::iterator v_begin = v.begin(); // 起始迭代器,指向第一个位置
        // vector::iterator v_end = v.end();     // 结束迭代器,指向最后一个元素的下一个位置
        // while (v_begin != v_end)
        // {
        //     cout << *v_begin << endl;
        //     v_begin++;
        // }
    
        
        // 第二种遍历方式
        // for (vector::iterator v_begin = v.begin(); v_begin < v.end(); v_begin++)
        // {
        //     cout << *v_begin << endl;
        // }
    
        
        // 第三种遍历方式,STL提供的标准遍历算法
        for_each(v.begin(), v.end(), my_print);
    
        system("pause");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    存放自定义数据类型
    #include 
    #include 
    #include 
    using namespace std;
    
    class Person
    {
    public:
        Person(string name, int age)
        {
            this->m_name = name;
            this->m_age = age;
        }
        string m_name;
        int m_age;
    };
    
    void test01()
    {
        // 存放Person对象
        vector<Person> v1;
        Person p1("zhangsan", 1);
        Person p2("lisi", 2);
        Person p3("wangwu", 3);
        v1.push_back(p1);
        v1.push_back(p2);
        v1.push_back(p3);
    
        for (vector<Person>::iterator it = v1.begin(); it < v1.end(); it++)
        {
            // 两种方式
            cout << "name:" << it->m_name << ",age:" << it->m_age << endl; // 指针
            // cout << "name:" << (*it).m_name << ",age:" << (*it).m_age << endl; // 对象
        }
    }
    
    void test02()
    {
        // 存放Person对象的地址
        vector<Person *> v2;
        Person p1("zhangsan", 1);
        Person p2("lisi", 2);
        Person p3("wangwu", 3);
        v2.push_back(&p1);
        v2.push_back(&p2);
        v2.push_back(&p3);
    
        for (vector<Person *>::iterator it = v2.begin(); it < v2.end(); it++)
        {
            // 解引用之后是Person*,对象的地址
            cout << "name:" << (*it)->m_name << ",age:" << (*it)->m_age << endl;
        }
    }
    
    int main()
    {
        test01();
        cout << "------------" << endl;
        test02();
    
        system("pause");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    容器嵌套容器
    #include 
    #include 
    using namespace std;
    
    int main()
    {
        vector<vector<int>> v;
    
        vector<int> v1;
        vector<int> v2;
        vector<int> v3;
    
        for (int i = 0; i < 3; i++)
        {
            v1.push_back(i + 1);
            v2.push_back(i + 4);
            v3.push_back(i + 7);
        }
    
        v.push_back(v1);
        v.push_back(v2);
        v.push_back(v3);
    
        for (vector<vector<int>>::iterator it = v.begin(); it < v.end(); it++)
        {
            for (vector<int>::iterator sub_it = (*it).begin(); sub_it < (*it).end(); sub_it++)
            {
                cout << *sub_it << "\t";
            }
        }//1    2   3   4   5   6   7   8   9
    
        system("pause");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    常用容器

    string

    • char* 是一个指针
    • string 是一个类,内部封装了char*,管理这个字符串,是一个char* 型的容器
    • string管理char*所分配到内存,不用担心越界问题
    1. string();
    2. string(const char* s);
    3. string(const string& str);
    4. string(int n,char c);
    int main()
    {
        // 1
        string s1;
    
        // 2
        const char *str = "hello cpp";
        string s2(str);
    
        // 3
        string s3(s2);
    
        // 4
        string s4(9, 'q');
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    string 赋值操作
        // string& operator=(const char* s);//char*类型字符串 赋值给当前的字符串
        // string& operator=(const string &s);//把字符串s赋给当前的字符串
        // string& operator=(char c) ;//字符赋值给当前的字符串
        // string& assign(const char *s) ;//把字符串s赋给当前的字符串
        // string& assign(const char *s, int n) ;//把字符串s的前n个字符赋给当前的字符串
        // string& assign(const string &s) ;//把字符串s赋给当前字符串
        // string& assign(int n, char c) ;//用n个字符c赋给当前字符串
    
    
        string str5;
        str5 = "hello python";
    
        string str6;
        str6 = str5;
    
        string str7;
        str7 = 'a';
    
        string str1;
        str1.assign("hello cpp");
    
        string str2;
        str2.assign("hello cpp", 5);
    
        string str3;
        str3.assign(str2);
    
        string str4;
        str4.assign(3, 'q');
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    string字符串拼接
    #include 
    #include 
    using namespace std;
    
    int main()
    {
       // string& operator+=(const char* str);//重载+=操作符
    
       // string& operator+=(const char c);//重载+=操作符
    
       // string& operator+=(const string& str);//重载+=操作符
    
       // string& 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 str1 = "我";
       str1 += "喜欢编程";
       cout << str1 << endl;
       str1 += '!';
       cout << str1 << endl;
    
       string str2 = "python,cpp";
       str1 += str2;
       cout << str1 << endl;
    
       string str3 = "I";
       str3.append(" like ");
       str3.append("program,game", 8);
       cout << str3 << endl;
       str3.append(str2, 7, 3); // 从第7个开始,截取3个字符
       cout << str3 << endl;
    
       system("pause");
    
       return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    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 string& str, int pos = npos) const;//查找str最后一次位置,从pos开始查找
    
    int rfind(const char* s, int pos = npos) const;//查找s最后一次出现位置,从pos开始查找
    
    int rfind(const char* s, int pos, int n) const;//从pos查找s的前n个字符最后一次位置
    
    int rfind(const char c, int pos = ) const;//查找字符C最后一次出现位置
    
    string& replace(int pos, int n, const string& str);//替换从pos开始n个字符为字符串str
    
    string& replace(int pos, int n,const char* s);//替换从pos开始的n个字符为字符串s
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    string字符串比较

    主要用于比较两个字符串是否相等,比较大小意义不大

    //大于返回1,小于返回-1,等于返回0
    int compare(const string &s) const;//与字符串比较
    
    int compare(const char *s) const;
    
    
    string str1 = "hello";
    string str2 = "abc";
    str1.compare(str2);//1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    string字符串存取
    char& operator[](int n);//[]取字符
    char& at(int n);//取字符
    
    string str1 = 'hello cpp';
    for(int i=0;i<str1.size();i++)
    {
        cout<<str1[i]<<",";
    }
    
    
    for(int i=0;i<str1.size();i++)
    {
        cout<<str1.at(i)<<",";
    }
    
    str1[0] = 'q';
    str1.at(1) = 'z';
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    string插入和删除
    string& insert(int pos, const char* s);//插入字符串
    
    string& insert(int pos, const string& str);//插入字符串
    
    string& insert(int pos, int n, char c);//在指定位置插入n个字符
    
    string& erase(int pos, int n = npos);//删除从Pos开始的n个字符
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    string子串
    string substr(int pos = 0,int n = npos) const;//返回由pos开始的n个字符组成的字符串
    
    • 1

    Vector

    vector和数组相似,也称为单端数组

    vector与普通数组的不同之处,vector可以动态扩展

    并不是在原空间之后续接空间,而是拷贝到更大的新空间,并删除原空间

    vector<T> v;//采用模板实现类实现,默认构造函数
    
    vector(v.begin(),v.end());//将 v[begin(),end()) 区间中的元素拷贝给本身
    
    vector(n,elem);//构造函数将n个elem拷贝给本身
    
    vector(const vector &vec);//拷贝构造函数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    构造函数
    #include 
    #include 
    using namespace std;
    
    void print_vector(vector<int> v)
    {
        for (vector<int>::iterator it = v.begin(); it < v.end(); it++)
        {
            cout << (*it) << ",";
        }
        cout << endl;
    }
    
    int main()
    {
        // 1 默认无参构造
        vector<int> v1;
        for (int i = 0; i < 10; i++)
        {
            v1.push_back(i);
        }
    
        print_vector(v1);
    
        // 2 区间
        vector<int> v2(v1.begin(), v1.end());
        print_vector(v2);
    
        // 3 n个元素
        vector<int> v3(10, 9);
        print_vector(v3);
    
        // 4 拷贝
        vector<int> v4(v3);
        print_vector(v4);
    
        system("pause");
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    赋值
    • =
    • assign()
    int main()
    {
        // 1 默认无参构造
        vector<int> v1;
        for (int i = 0; i < 10; i++)
        {
            v1.push_back(i);
        }
         
        // 赋值
        vector<int> v5;
        v5 = v1;
    
        vector<int> v6;
        v6.assign(v5.begin(), v5.end());
    
        vector<int> v7;
        v7.assign(10, 1);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    vector容量和大小

    容量capacity自动扩容

    empty();//判断容器是否为空
    
    capacity();//容器的容量
    
    size();//返回容器中元素的个数
    
    resize(int num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置//如果容器变短,则未尾超出容器长度的元素被删除。
    
    resize(int num, elem);//重新指定容器的长度为num,若容器变长,则以elem值填充新位置//如果容器变短,则末尾超出容器长度的元素被删除
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    #include 
    #include 
    using namespace std;
    
    void print_vector(vector<int> v)
    {
        for (vector<int>::iterator it = v.begin(); it < v.end(); it++)
        {
            cout << (*it) << ",";
        }
        cout << endl;
    }
    
    int main()
    {
        vector<int> v;
        for (int i = 0; i < 10; i++)
        {
            v.push_back(i);
        }
        print_vector(v);                           // 0,1,2,3,4,5,6,7,8,9,
        cout << "是否为空:" << v.empty() << endl; // 0
        cout << "容量:" << v.capacity() << endl;  // 16
        cout << "大小:" << v.size() << endl;      // 10
    
        // 重设大小,指定填充的值,默认0填充
        v.resize(15, -1);
        print_vector(v);                          // 0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,
        cout << "容量:" << v.capacity() << endl; // 16
        cout << "大小:" << v.size() << endl;     // 15
    
        //更短的话,多的数直接删除
        v.resize(6);
        print_vector(v);                          // 0,1,2,3,4,5
        cout << "容量:" << v.capacity() << endl; // 16
        cout << "大小:" << v.size() << endl;     // 6
    
        v.resize(30);
        print_vector(v);                          // 0,1,2,3,4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        cout << "容量:" << v.capacity() << endl; // 30
        cout << "大小:" << v.size() << endl;     // 30
    
        system("pause");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    插入和删除
    push back(ele);//尾部插入元素ele
    
    pop_back();//删除最后一个元素
    
    insert(const iterator pos, ele);//选代器指向位置pos插入元素ele
    
    insert(const_iterator pos,int count,ele); //选代器指向位置pos插入count个元素ele//删除迭代器指向的元素erase(const iterator pos);
    
    erase(const_iterator start,const iterator end); //删除选代器从start到end之间的元素
    
    clear();//删除容器中所有元素
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    #include 
    #include 
    using namespace std;
    
    void print_vector(vector<int> v)
    {
        for (vector<int>::iterator it = v.begin(); it < v.end(); it++)
        {
            cout << (*it) << ",";
        }
        cout << endl;
    }
    
    int main()
    {
        vector<int> v;
        v.push_back(10);
        v.push_back(20);
        v.push_back(30);
        v.push_back(40);
        v.push_back(50);
        print_vector(v); // 10,20,30,40,50,
    
        // 尾部删除
        v.pop_back();
        print_vector(v); // 10,20,30,40,
    
        // 插入,第一个参数是迭代器
        v.insert(v.begin(), 0);
        print_vector(v); // 0,10,20,30,40,
    
        v.insert(v.begin(), 5, -1); //-1,-1,-1,-1,-1,0,10,20,30,40,
        print_vector(v);
    
        // 删除,参数是迭代器
        v.erase(v.begin());
        print_vector(v); //-1,-1,-1,-1,0,10,20,30,40,
    
        // 清空1
        v.erase(v.begin(), v.end());
        print_vector(v);
    
        // 清空2
        v.clear();
        print_vector(v);
    
        system("pause");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    数据存取
    at(int idx);//返回索引idx所指的数据
    
    operator[];//返回索引idx所指的数据
    
    front();//返回容器中第一个数据元素
    
    back();//返回容器中最后一个数据元素
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    int main()
    {
        vector<int> v;
        v.push_back(10);
        v.push_back(20);
        v.push_back(30);
    	
        cout<<v[1]<<endl;//20
        
        cout<<v.at(2)<<endl;//30
        
        cout<<v.front()<endl;//10
        
        cout<<v.back()<<endl;//30
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    vector互换容器

    swap

    int main()
    {
        vector<int> v1;
        for (int i = 0; i < 10; i++)
        {
            v1.push_back(i);
        }
        print_vector(v1); // 0,1,2,3,4,5,6,7,8,9,
    
        vector<int> v2;
        for (int i = -10; i < 0; i++)
        {
            v2.push_back(i);
        }
        print_vector(v2); //-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,
    
        // 交换容器
        v1.swap(v2);
        print_vector(v1); //-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,
        print_vector(v2); // 0,1,2,3,4,5,6,7,8,9,
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    实际用途:收缩内存空间

    // vector(v)是一个匿名对象,交换空间之后,匿名对象指向原来的大容量容器,
    // 匿名对象在执行完之后被系统自动回收,起到压缩空间的作用
    vector(v).swap(v);
    
    • 1
    • 2
    • 3
    void test()
    {
        vector<int> v;
        cout << "容量:" << v.capacity() << endl; // 0
        cout << "大小:" << v.size() << endl;     // 0
        
        for (int i = 0; i < 10000; i++)
        {
            v.push_back(i);
        }
        cout << "容量:" << v.capacity() << endl; // 16384
        cout << "大小:" << v.size() << endl;     // 10000
    
        // 重设容器大小,容量并不会变,但是只用到非常少的空间
        v.resize(10);
        cout << "容量:" << v.capacity() << endl; // 16384
        cout << "大小:" << v.size() << endl;     // 10
    
        // 利用swap压缩空间
        // vector(v)是一个匿名对象,交换空间之后,匿名对象指向原来的大容量容器,
        // 匿名对象在执行完之后被系统自动回收,起到压缩空间的作用
        vector<int>(v).swap(v);
        cout << "容量:" << v.capacity() << endl; // 10
        cout << "大小:" << v.size() << endl;     // 10
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    vector预留空间
    reserve(int len); //容器预留len个元素长度,预留位置不初始化,元素不可访问
    
    • 1
    统计容器在插入数据时新开辟内存的次数
    void test()
    {
        vector<int> v;
        cout << "容量:" << v.capacity() << endl; // 0
        cout << "大小:" << v.size() << endl;     // 0
    
        int num = 0;
        int *p = NULL; // 指针p指向容器的首元素地址,容器每次扩容都要新开辟内存,首元素地址就会变
        for (int i = 0; i < 10000; i++)
        {
            v.push_back(i);
            if (p != &v[0])
            {
                num++;
                p = &v[0];
            }
        }
        cout << "容量:" << v.capacity() << endl; // 16384
        cout << "大小:" << v.size() << endl;     // 10000
        cout << "开辟次数:" << num << endl;      // 15
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    当容器需要插入很大数据量时

    容器开始没有预留空间,插入数据需要自动扩容,如果我们一直开始就知道要插入的元素个数,就可以利用reserve()预留空间,减少空间开辟的次数。上面的代码需要扩容15次,下面的代码只需要扩容1次

    
    void test()
    {
        vector<int> v;
        cout << "容量:" << v.capacity() << endl; // 0
        cout << "大小:" << v.size() << endl;     // 0
    
        v.reserve(10000);
        cout << "容量:" << v.capacity() << endl; // 10000
        cout << "大小:" << v.size() << endl;     // 0
    
        int num = 0;
        int *p = NULL; // 指针p指向容器的首元素地址,容器每次扩容都要新开辟内存,首元素地址就会变
        for (int i = 0; i < 10000; i++)
        {
            v.push_back(i);
            if (p != &v[0])
            {
                num++;
                p = &v[0];
            }
        }
        cout << "容量:" << v.capacity() << endl; // 16384
        cout << "大小:" << v.size() << endl;     // 10000
        cout << "开辟次数:" << num << endl;      // 1
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    deque

    双端数组,头部和尾部都可以操作

    deque与vector区别:

    • vector对于头部的插入删除效率低,数据量越大,效率越低。
    • deque相对而言,对头部的插入删除速度回比vector快。
    • vector访问元素时的速度会比deque快,这和两者内部实现有关
    • deque也是支持随机访问的
    deque<T> degT;//默认构造形式
    
    deque( beg, end);//构造函数将[beg,end)区间中的元素拷贝给本身
    
    deque(n, elem);//构造函数将n个elem拷贝给本身
    
    deque(const deque &deq);//拷贝构造函数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    构造函数
    #include 
    #include 
    using namespace std;
    
    //限制为只读
    void print_deque(const deque<int>& d)
    {
        for (deque<int>::const_iterator it = d.begin(); it < d.end(); it++)
        {
            // *it = 10;//不能修改
            cout << (*it) << " ";
        }
        cout << endl;
    }
    
    int main()
    {
        deque<int> d;
        for (int i = 0; i < 10; i++)
        {
            d.push_front(i); // 前插
        }
        for (int i = 10; i < 20; i++)
        {
            d.push_back(i); // 后插
        }
        print_deque(d);
    
        //区间
        deque<int> d2(d.begin(), d.end());
        print_deque(d2);
    
        //10个1
        deque<int> d3(10, 1);
        print_deque(d3);
    
        //拷贝构造
        deque<int> d4(d3);
        print_deque(d4);
    
        system("pause");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    赋值
    deque& operator=(const deque &deq);//重载等号操作符
    assign(beg, end);//将[beg,end)区间中的数据拷贝赋值给本身
    assign(n, elem);//将n个elem拷贝赋值给本身。
    
    • 1
    • 2
    • 3
    大小的操作

    deque没有容量的概念,它是不限容量的

    deque.empty();//判断容器是否为空
    deque.size();//返回容器中元素的个数
    deque.resize(num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置//如果容器变短,则末尾超出容器长度的元素被删除
    deque.resize(num, elem);//重新指定容器的长度为num,若容器变长,则以elem值填充新位置//如果容器变短,则末尾超出容器长度的元素被删除,
    
    • 1
    • 2
    • 3
    • 4
    插入和删除
    两端插入操作:
    push_back(elem);//在容器尾部添加一个数据
    push_front(elem);//在容器头部插入一个数据
    pop_back();//删除容器最后一个数据
    pop_front();//删除容器第一个元素
    
    指定位置操作:
    insert(pos,elem);//在pos位置插入一个elem元素的拷贝,返回新数据的位置,pos是迭代器对象
    insert(pos,n,elem);//在pos位置插入n个elem数据,无返回值
    insert(pos, beg,end);//在pos位置插入[beg,end)区间的数据,无返回值,pos,beg,end是迭代器对象
    clear();//清空容器的所有数据
    erase(beg,end);//删除[beg,end)区间的数据,返回下一个数据的位置,beg,end是迭代器对象
    erase(pos);//删除pos位置的数据,返回下一个数据的位置,参数是迭代器
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    存取
    at(int idx);//返回索idx所指的数据
    operator[];//返回索引idx所指的数据
    front();//返回容器中第一个数据元素
    back();//返回容器中最后一个数据元素
    
    • 1
    • 2
    • 3
    • 4
    排序

    STL提供的排序函数

    sort(iterator begin,iterator end);//对区间内的元素进行排序
    
    • 1

    对于支持随机访问的迭代器容器,都可以利用sort算法进行排序

    #include 
    #include 
    #include 
    using namespace std;
    
    // 限制为只读
    void print_deque(const deque<int> &d)
    {
        for (deque<int>::const_iterator it = d.begin(); it < d.end(); it++)
        {
            // *it = 10;//不能修改
            cout << (*it) << " ";
        }
        cout << endl;
    }
    
    int main()
    {
        deque<int> d;
        for (int i = 0; i < 10; i++)
        {
            d.push_front(i); // 前插
        }
        for (int i = 10; i < 20; i++)
        {
            d.push_back(i); // 后插
        }
        print_deque(d);//9 8 7 6 5 4 3 2 1 0 10 11 12 13 14 15 16 17 18 19
        
        //STL提供的排序算法,默认升序
        sort(d.begin(), d.end());
        print_deque(d);//0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    stack

    栈:先进后出

    构造函数:
    stack<T> stk;//stack采用模板类实现, stack对象的默认构造形式
    stack(const stack &stk);//拷贝构造函数
    
    赋值操作:
    stack& operator=(const stack &stk);//重载等号操作符
    
    数据存取:
    push(elem);//向栈顶添加元素
    pop();//从栈顶移除第一个元素
    top();//返回栈顶元素
    
    大小操作:
    empty();//判断堆栈是否为空
    size();//返回栈的大小
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    #include 
    #include 
    using namespace std;
    
    int main()
    {
    
        stack<int> s;
        s.push(1);
        s.push(3);
        s.push(4);
        s.push(5);
    
        cout << "size:" << s.size() << endl;
    
        while (!s.empty())
        {
            cout << s.top() << endl;
            s.pop();
        }
    
        cout << "size:" << s.size() << endl;
    
        system("pause");
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    queue

    队列:先进先出

    构造函数:
    queue<T> que;//queue采用模板类实现,queue对象的默认构造形式
    queue(const queue &que);//拷贝构造函数
    
    赋值操作:
    queue& operator=(const queue &que);//重载等号操作符
    
    数据存取:
    push(elem);//往队尾添加元素
    pop();//从队头移除第一个元素
    back();//返回最后一个元素
    front();//返回第一个元素
    
    大小操作:
    empty();//判断堆栈是否为空
    size();//返回栈的大小
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    list

    链表:链式存储。

    • 适合插入和删除操作
    • 遍历速度没有数组快
    • 占用空间比数组大

    STL中的链表是一个双向循环链表

    • 动态分配内存,不会造成内存浪费和溢出
    • 链表插入和删除不用移动大量元素
    • 插入和删除操作不会造成原有的迭代器失效,而在vector中不成立
    构造函数
    list<T> lst;//list采用采用模板类实现,对象的默认构造形式
    list(beg,end);//构造函数将[beg,end)区间中的元素拷贝给本身
    list(n,elem);//构造函数将n个elem拷贝给本身
    list(const list &lst);//拷贝构造函数。
    
    • 1
    • 2
    • 3
    • 4
    #include 
    #include 
    using namespace std;
    
    void print_list(const list<int> &li)
    {
        for (list<int>::const_iterator it = li.begin(); it != li.end(); it++)
        {
            cout << (*it) << " ";
        }
        cout << endl;
    }
    
    int main()
    {
        list<int> li;
        li.push_back(2);
        li.push_back(5);
        li.push_back(7);
        li.push_back(8);
        print_list(li);
    
        list<int> li2(li.begin(), li.end());
        print_list(li2);
    
        list<int> li3(10, -1);
        print_list(li3);
    
        list<int> li4(li3);
        print_list(li4);
    
        system("pause");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    赋值和交换
    assign(beg, end);//将[beg, end)区间中的数据拷贝赋值给本身
    assign(n, elem);//将n个elem拷贝赋值给本身
    list& operator=(const list &lst);//重载等号操作符
    swap(lst);//将lst与本身的元素互换
    
    • 1
    • 2
    • 3
    • 4
    大小操作
    size();//返回容器中元素的个数
    empty();//判断容器是否为空
    resize(num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置//如果容器变短,则末尾超出容器长度的元素被删除。
    resize(num, elem);//重新指定容器的长度为num,若容器变长,则以elem值填充新位置//如果容器变短,则末尾超出容器长度的元素被删除。
    
    • 1
    • 2
    • 3
    • 4
    插入和删除
    push_back(elem);//在容器尾部加入一个元素
    pop_back();//删除容器中最后一个元素
    push_front(elem);//在容器开头插入一个元素
    pop_front()://从容器开头移除第一个元素
    insert(pos,elem);//在pos位置插elem元素的拷贝,返回新数据的位置
    insert(pos,n,elem);//在pos位置插入n个elem数据,无返回值.
    insert(pos,begend);//在pos位置插入[beg,end)区间的数据,无返回值.
    clear();//移除容器的所有数据
    erase(beg,end);//删除[beg,end)区间的数据,返回下一个数据的位置
    erase(pos);//删除pos位置的数据,返回下一个数据的位置
    remove(elem);//删除容器中所有与elem值匹配的元素
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    存取

    链表只能对首尾元素进行存取,不能随机访问

    list是双向链表,利用迭代器,++,–

    front();//返回第一个元素
    back();//返回最后一个元素
    
    • 1
    • 2
    反转和排序

    不支持随机访问的数据结构,不可以使用标准算法,例如sort

    sort(li.begin(),li.end()),不允许的,list不支持随机访问

    reverse();//反转
    sort();//排序
    
    • 1
    • 2
    #include 
    #include 
    using namespace std;
    
    void print_list(const list<int> &li)
    {
        for (list<int>::const_iterator it = li.begin(); it != li.end(); it++)
        {
            cout << (*it) << " ";
        }
        cout << endl;
    }
    
    bool my_compare(int a, int b)
    {
        return a > b;
    }
    
    int main()
    {
    	// 升序
        li.sort();
        print_list(li);
    
        // 降序,需要提供一个降序的函数名
        li.sort(my_compare);
        print_list(li);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    案例-list-自定义类型排序

    自定义数据类型需要指定排序规则

    #include 
    #include 
    #include 
    using namespace std;
    
    class Person
    {
    public:
        Person(string name, int age, int height)
        {
            this->m_age = age;
            this->m_name = name;
            this->m_height = height;
        }
    
        string m_name;
        int m_age;
        int m_height;
    };
    
    void print_Person(const list<Person> &lp)
    {
        for (list<Person>::const_iterator it = lp.begin(); it != lp.end(); it++)
        {
            cout << "name:" << it->m_name << ",age:" << it->m_age << ",height:" << it->m_height << endl;
        }
    }
    
    // 指定排序规则
    bool person_compare(Person &p1, Person &p2)
    {
        // 年龄不相等,按照年龄升序
        if (p1.m_age != p2.m_age)
        {
            return p1.m_age < p2.m_age;
        }
        else // 年龄相等,按照身高降序
        {
            return p1.m_height > p2.m_height;
        }
    }
    
    int main()
    {
        Person p1("张飞", 20, 180);
        Person p2("赵云", 29, 174);
        Person p3("悟空", 1000, 170);
        Person p4("武松", 29, 173);
        Person p5("张三", 29, 190);
    
        list<Person> L_person;
        L_person.push_back(p1);
        L_person.push_back(p2);
        L_person.push_back(p3);
        L_person.push_back(p4);
        L_person.push_back(p5);
    
        print_Person(L_person);
    
        //指定排序规则
        L_person.sort(person_compare);
        print_Person(L_person);
    
        system("pause");
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    setmultiset

    • 所有元素在插入时会自动排序
    • setmulti属于关联式容器,底层结构是二叉树
    • set不允许有重复的元素
    • multiset可以有重复的元素
    //构造函数
    set<T> s;//默认构造
    set<T>(const set& s);//拷贝构造
    
    • 1
    • 2
    • 3
    大小
    #include 
    #include 
    using namespace std;
    
    void print_set(set<int> s)
    {
        for (set<int>::iterator it = s.begin(); it != s.end(); it++)
        {
            cout << (*it) << " ";
        }
        cout << endl;
    }
    
    int main()
    {
        set<int> s;
        s.insert(1);
        s.insert(4);
        s.insert(2);
        s.insert(9);
        s.insert(2);  // 不能重复,不会报错
        print_set(s); // 1 2 4 9
    
        cout << "是否为空:" << s.empty() << endl;
        cout << "大小:" << s.size() << endl;
    
        set<int> s2;
        s2.insert(-1);
        s2.insert(-5);
        s2.insert(-3);
    
        // 交换
        s.swap(s2);
        print_set(s); //-5 - 3 - 1
    
        system("pause");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    插入和删除
    insert(elem);//在容器中插入元素。
    clear();//清除所有元素
    erase(pos);//删除pos迭代器所指的元素,返回下一个元素的迭代器.
    erase(beg,end);//删除区间[beg,end)的所有元素,返回下一个元素的迭代器
    erase(elem);//删除容器中值为elem的元素
    
    • 1
    • 2
    • 3
    • 4
    • 5
    查找和统计
    find(key);//查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end();
    count(key);//统计key的元素个数
    
    • 1
    • 2
    setmultiset区别
     #include 
    #include 
    using namespace std;
    
    int main()
    {
        set<int> s;
        pair<set<int>::iterator, bool> ret = s.insert(1); // ret对象,分别返回插入后迭代器的位置和是否插入成功
        if (ret.second)                                   //   取第二个参数
        {
            cout << "插入成功" << endl; // 输出
        }
        else
        {
            cout << "插入失败" << endl;
        }
    
        ret = s.insert(1); // 第二次插入失败
        if (ret.second)
        {
            cout << "插入成功" << endl;
        }
        else
        {
            cout << "插入失败" << endl; // 输出
        }
    
        multiset<int> ms;
        ms.insert(1);
        ms.insert(2);
        ms.insert(5);
        ms.insert(2);
        ms.insert(2);
        for (multiset<int>::iterator it = ms.begin(); it != ms.end(); it++)
        {
            cout << (*it) << " ";
        }
        cout << endl; // 1 2 2 2 5
    
        system("pause");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    pair队组
    pair<type, type> p ( value1, value2 );
    pair<type, type> p = make pair( value1, value2 );
    
    • 1
    • 2
    #include 
    #include 
    using namespace std;
    
    int main()
    {
        // 1
        pair<string, int> p1("zhangsan", 17);
        cout << p1.first << "," << p1.second << endl;
    
        // 2
        pair<string, int> p2 = make_pair("lisi", 10);
        cout << p2.first << "," << p2.second << endl;
    
        system("pause");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    改变set排序规则

    利用仿函数修改排序规则

    #include 
    #include 
    using namespace std;
    
    // 自定义排序规则
    class MyCompare
    {
    public:
        // 仿函数
        bool operator()(int v1, int v2)
        {
            return v1 > v2;
        }
    };
    
    int main()
    {
        set<int, MyCompare> s;
        s.insert(1);
        s.insert(4);
        s.insert(2);
        s.insert(7);
        s.insert(9);
    
        for (set<int, MyCompare>::iterator it = s.begin(); it != s.end(); it++)
        {
            cout << (*it) << " ";
        }
        cout << endl; // 9 7 4 2 1
    
        system("pause");
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    自定义数据类型插入set,需要指定排序规则

    #include 
    #include 
    using namespace std;
    
    class Person
    {
    public:
        Person(string name, int age)
        {
            this->m_age = age;
            this->m_name = name;
        }
        string m_name;
        int m_age;
    };
    
    class personCompare
    {
    public:
        // 仿函数
        bool operator()(const Person &p1, const Person &p2)
        {
            // 按照年龄降序
            return p1.m_age > p2.m_age;
        }
    };
    
    int main()
    {
        // 自定义数据类型存储在set容器中,需要指定排序规则
        Person p1("悟空", 300);
        Person p2("刘备", 15);
        Person p3("莎莎", 26);
        Person p4("王四", 72);
        Person p5("呼呼", 50);
    
        // 需要指定仿函数的类
        set<Person, personCompare> s_person;
        s_person.insert(p1);
        s_person.insert(p2);
        s_person.insert(p3);
        s_person.insert(p4);
        s_person.insert(p5);
        for (set<Person, personCompare>::iterator it = s_person.begin(); it != s_person.end(); it++)
        {
            cout << "name:" << it->m_name << ",age:" << it->m_age << endl;
        }
    
        system("pause");
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    mapmultimap

    map中所有的元素都是pair,根据键排序,不允许重复

    multimap可以重复

    #include 
    #include 
    #include 
    using namespace std;
    
    void print_map(map<int, string> &m)
    {
        for (map<int, string>::iterator it = m.begin(); it != m.end(); it++)
        {
            cout << (*it).first << " : " << it->second << endl;
        }
    }
    
    int main()
    {
        map<int, string> m;
        // 插入时会按照key排序
        m.insert(pair<int, string>(1, "aaa"));
        m.insert(pair<int, string>(4, "bbb"));
        m.insert(pair<int, string>(3, "ccc"));
        m.insert(pair<int, string>(2, "ddd"));
        print_map(m);
    
        // 拷贝构造
        map<int, string> m2(m);
        print_map(m2);
    
        system("pause");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    大小
    size();//大小
    empty();//是否为空
    swap(m);//交换
    
    • 1
    • 2
    • 3
    插入和删除
    insert(elem);//在容器中插入元素
    clear();//清除所有元素
    erase(pos);//删除pos迭代器所指的元素,返回下一个元素的迭代器
    erase(beg, end);//删除区间[beg,end)的所有元素,返回下一个元素的选代器
    erase(key);//删除容器中值为kev的元素
    
    • 1
    • 2
    • 3
    • 4
    • 5
        // 插入,4种插入方法
        map<int, int> m3;
        m3.insert(pair<int, int>(1, -1));
        m3.insert(make_pair(2, -2));
        m3.insert(map<int, int>::value_type(3, -3));
        m3[4] = -4;// 但通常是用来取数
        for (map<int, int>::iterator it = m3.begin(); it != m3.end(); it++)
        {
            cout << (*it).first << " : " << it->second << endl;
        }
    
        // 删除
        m3.erase(3);                    // 按照key删除
        m3.erase(m3.begin());           // 删除迭代器未知的元素
        m3.erase(m3.begin(), m3.end()); // 删除迭代器区间的元素
        m3.clear();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    查找和统计
    find(key);//查找key是否存在,若存在,返回该键的元素的迭代器; 若不存在,返回set.end();
    count(key);//统i计key的元素个数
    
    • 1
    • 2
    排序-修改排序规则
    // 自定义排序规则
    class MyCompare
    {
    public:
        bool operator()(int v1, int v2)
        {
            return v1 > v2;
        }
    };
    
    
    int main()
    {
        // 自定义排序规则-降序
        map<int, int, MyCompare> m_c;
        m_c.insert(make_pair(1, -1));
        m_c.insert(make_pair(3, -3));
        m_c.insert(make_pair(2, -2));
        m_c.insert(make_pair(4, -4));
        for (map<int, int, MyCompare>::iterator it = m_c.begin(); it != m_c.end(); it++)
        {
            cout << "key=" << it->first << ",value=" << it->second << endl;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    案例-员工分组
    #include 
    #include 
    #include 
    #include 
    #include 
    using namespace std;
    
    // 10个员工,随机分配到 测试,研发,美术部门,随机分配工资
    // 分别显示每个部门的员工信息
    
    #define CESHI 0
    #define YANFA 1
    #define MEISHU 2
    
    class Employee
    {
    public:
        Employee(string name, int salary)
        {
            this->m_name = name;
            this->m_salary = salary;
        }
        string m_name;
        int m_salary;
    };
    
    // 创建10名员工
    void create_employee(vector<Employee> &v_emp)
    {
        string name_arr = "ABCDEFGHIJ";
        for (int i = 0; i < 10; i++)
        {
            int sal = rand() % 10000 + 10000;
            Employee emp(name_arr.substr(i, 1), sal);
            v_emp.push_back(emp);
        }
    }
    
    // 打印员工
    void print_employee(const vector<Employee> &v_emp)
    {
        for (vector<Employee>::const_iterator it = v_emp.begin(); it != v_emp.end(); it++)
        {
            cout << "name:" << it->m_name << ",salary:" << it->m_salary << endl;
        }
    }
    
    // 分组到不同部门
    void group_department(const vector<Employee> &v_emp, multimap<int, Employee> &m_emp)
    {
        for (vector<Employee>::const_iterator it = v_emp.begin(); it != v_emp.end(); it++)
        {
            // 给当前遍历的员工随机分配一个部门
            int dep_id = rand() % 3;                // 0,1,2
            m_emp.insert(make_pair(dep_id, (*it))); // 部门编号-员工
        }
    }
    
    // 打印不同部门的员工信息
    void print_department_employee(const multimap<int, Employee> &m_emp)
    {
        cout << "测试:" << endl;
        multimap<int, Employee>::const_iterator start_ceshi = m_emp.find(CESHI); // 返回迭代器的位置
        int count_ceshi = m_emp.count(CESHI);                                    // 测试部门的员工数量
        for (int index = 0; start_ceshi != m_emp.end() && index < count_ceshi; start_ceshi++, index++)
        {
            cout << "name:" << start_ceshi->second.m_name << ",salary:" << start_ceshi->second.m_salary << endl;
        }
    
        cout << "研发:" << endl;
        multimap<int, Employee>::const_iterator start_yanfa = m_emp.find(YANFA); // 返回迭代器的位置
        int count_yanfa = m_emp.count(YANFA);                                    // 研发部门的员工数量
        for (int index = 0; start_yanfa != m_emp.end() && index < count_yanfa; start_yanfa++, index++)
        {
            cout << "name:" << start_yanfa->second.m_name << ",salary:" << start_yanfa->second.m_salary << endl;
        }
    
        cout << "美术:" << endl;
        multimap<int, Employee>::const_iterator start_meishu = m_emp.find(MEISHU); // 返回迭代器的位置
        int count_meishu = m_emp.count(MEISHU);                                    // 美术部门的员工数量
        for (int index = 0; start_meishu != m_emp.end() && index < count_meishu; start_meishu++, index++)
        {
            cout << "name:" << start_meishu->second.m_name << ",salary:" << start_meishu->second.m_salary << endl;
        }
    }
    
    int main()
    {
        // 利用系统时间设置随机数
        srand((unsigned int)time(NULL));
    
        vector<Employee> v_emp;
        create_employee(v_emp);
        print_employee(v_emp);
    
        // 分组到不同部门
        multimap<int, Employee> m_emp;
        group_department(v_emp, m_emp);
        print_department_employee(m_emp);
    
        system("pause");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
  • 相关阅读:
    Linux用户增删改查,修改密码,查看当前用户ID,切换用户
    Python安装教程
    关于eBPF与可观测性,你想知道的都在这里
    设计模式-模板方法模式
    智慧林业云巡平台 客户端和移动端(支持语音和视频)自动定位巡护,后端离线路线监测
    如何设计静态资源缓存方案
    C语言指针进阶(2)
    2022上行部落的学习和实践总结
    一文读懂 C++11 中的匿名函数(也称lambda函数、lambda表达式)
    Python入门之经典函数实例
  • 原文地址:https://blog.csdn.net/weixin_46250447/article/details/134277119