C++学习笔记(B站黑马程序员)

1.简单案例

#include<iostream>
using namespace std;
int  main() {
    cout << "hello world" << endl;
    system("pause");
    return 0;
}

2.变量存在的意义:方便管理内存空间

3.变量

#include<iostream>
using namespace std;
int  main() {
    int a = 10;
    cout << "a="<<a<< endl;
    system("pause");
    return 0;
}

4.常量

#define 常量名 常量值

const 数据类型 常量名=常量值

#include<iostream>
using namespace std;
#define Day 7
int  main() {
    int a = 10;
    cout <<"一周共有:" << Day<<"天" << endl;
    system("pause");
    return 0;
}

5.数据类型:给变量分配合适的内存空间

短整型(short):-32768~32767

整型(int)

长整型(long)

长长整型(longlong)

6.sizeof关键字可以统计数据类型所占内存大小

7.float单精度 (7位)

double双精度(15-16位)

两者的区别在于表示的有效数字范围不同

8.科学计数法

#include<iostream>
using namespace std;
#define Day 7
int  main() {
    float f1 = 3e2;//3*10^2
    cout <<"f1:" <<f1<< endl;
    float f2 = 3e-2;//3*0.1^2
    cout << "f1: " << f2 << endl;
    system("pause");
    return 0;
}

9.字符型:字符型变量用于显示单个字符

char ch='a';
//单引号,不用双引号
//单引号内只能有一个字符,不可以是字符串
//a:97 A:65

10.转义字符

\a  警报
\b 退格,将当前位置移到前一列
\n 换行,将当前位置移到下一行开头
\\代表一个反斜线字符“\”
\t水平制表      更整齐的输出数据
\r 将当前位置移到本行开头

11.字符串型

C风格字符串
char str[]="hello world"//必须有中括号后面才能双引号;
C++风格字符串
string str2="hello world";
//包含一个头文件,#include<string>

12.布尔类型bool

bool flag = true;
    cout << flag << endl;//输出为1
//本质上1代表真的值,0代表假的值
 cout << "bool类型所占空间" << sizeof(bool) << endl;输出为1

13.数据的输入

int a = 0;
    cout << "请给整型变量进行赋值" << endl;
    cin >> a;
    cout <<"整型变量a="<< a << endl;
bool flag = false;
    cout << "请给布尔类型flag赋值" << endl;
    cin >> flag;
    cout << "布尔类型flag= " << flag << endl;//任意非0数值均为1

14.运算符

算术运算符
int a1 = 10;
    int b1 = 3;
    cout << a1 / b1 << endl;
//两个整数相除,结果依然是整数,将小数部分去除
//取模运算时,除数也不能为0
//两个小数不可以取模

//前置递增先对变量进行++,再计算表达式
//后置递增先计算表达式,后对变量进行++

15.赋值运算符

将表达式的值赋给变量
int a = 10;
    a -= 2;
    cout << "a= " << a << endl;

16.逻辑运算符

int a = 10;
    int b = 10;
    cout <<!a<< endl;
    cout << (a && b) << endl;
    cout << (a || b) << endl;

17.三目运算符

表达式1?表达式2:表达式
在C++三目运算符返回的是变量,可以继续赋值

18.switch语句

switch(表达式)
{
case 结果1:执行语句;break;
default:执行语句;break;
}
//switch判断时候只能是整型或者字符串,不可以是一个区间
//结构清晰,执行效率高
int num = rand() % 100 + 1;
        cout << num << endl;//系统生成随机数
srand((unsigned int)time(NULL));
#include<ctime>
do while会先执行一次循环语句,再判断循环条件
//水仙花数  
  int num = 100;
    do {
        int a = 0;
        int b = 0;
        int c = 0;
        a = num % 10;//个位
        b = num / 10 % 10;//十位
        c = num / 100;//百位
        if (a * a * a + b * b * b + c * c * c==num) {
            cout << num << endl;
        }
        num++;
    } while (num < 1000);

19.for循环

外层执行一次,内层执行一周
//九九乘法表
int  main() {
    for (int i = 1; i < 10; i++)
    {
        for (int j = 1; j <= i; j++) {
            cout << j << "*" << i << "=" << j * i << " ";
        }
        cout << endl;
    }
    system("pause");
    return 0;
}

20.跳转语句

break使用时机
出现在switch语句
出现在循环语句中
出现在嵌套循环语句中 
continue:跳过本次循环中余下尚未执行的语句,继续执行下一次循环
for (int i = 0; i <= 100; i++) {
        if (i % 2 == 0) {
            continue;
        }
    }
//输出全为奇数

备注:continue不会使整个循环而终止,而break会跳出循环

21.goto语句

可以无条件跳转语句
//goto语句
    cout << "1,xxxx" << endl;
    goto FLAG;
    cout << "2,xxxx" << endl;
    FLAG:
    cout << "3,xxxx" << endl;

22.数组

//所谓数组,就是一个集合,里面存放了相同类型的数据元素
数组是由连续的内存位置组成的
数组中的每个数据元素都是相同的数据类型
数据类型 数组名[数组长度];
数据类型 数组名[数组长度={值1,值2,...};
数据类型 数组名[]
数组名用途
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    cout << "整个数组所占内存: " << sizeof(arr) << endl;
    cout << "每个元素所占内存空间: " << sizeof(arr[0]) << endl;
    cout << "数组的元素个数为:" << sizeof(arr) / sizeof(arr[0]) << endl;
    cout << "数组中第一个元素的地址: " << (int) & arr[1] << endl;
数组名是常量不可以赋值
可以通过数组名获取到数组首地址
可以获取整个数组占用内存空间大小
//二维数组定义方式
数据类型 数组名[行数][列数]
数据类型 数组名[行数][列数]={{数据1,数据2},{数据3,数据4}}
数据类型 数组名[行数][列数]={数据1,数据2,数据3,数据4}
数据类型 数组名[][列数]={数据1,数据2,数据3,数据4}

23.函数

值传递时,如果形参发生改变,并不会影响实参
函数的声明:提前告诉编译器的存在
声明可以多次,但是定义只能有一次
函数的分文件编写
创建.h后缀名的头文件
创建.cpp后缀名的源文件
在头文件中写函数的声明
在源文件中写函数的定义
#include "swap.h"

24.指针

指针就是一个地址
在32位操作系统下,指针是占4个字节空间大小,不管是什么数据类型
在64位操作系统下,指针是8个字节
空指针:指针变量指向内存中编号为0的空间
空指针用于给指针变量进行初始化;
空指针是不可以进行访问的
int *p=NULL;
指针变量指向非法的内存空间
int *p=(int *)0x1100;

25.const

const修饰指针--常量指针
//指针的指向可以修改,但是指针指向的值不可以改
const int *p=&a;
const修饰常量--指针常量
int * const p=&a;
//指针的指向不可以改,指针指向的值可以改
const既修饰指针,又修饰常量
//指针的指向和指针指向的值都不可以改

26.指针和数组

如果不想修改实参,就用值传递,如果想修改实参,就用地址传递
int main() {
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    cout << "第一个元素为:" << arr[0] << endl;
    int* p = arr;
    cout << "利用指针访问第一个元素" << *p << endl;
    p++;
    cout << "利用指针访问第二个元素" << *p << endl;

    int* p2 = arr;
    for (int i = 0; i < 10; i++) {
        cout << *p2 << endl;
        p2++;
        }
    system("pause");
    return 0;
}

27.结构体

结构体属于用户自定义的数据类型,允许用户存储不用的数据类型

struct Student {
    string name;
    int age;
    int score;
    };
struct Student s2 = {};
int main() {
    struct Student s1;
    s1.name = "张三";
    s1.age = 18;
    s1.score = 100;
    cout << "姓名:" << s1.name << " 年龄:" << s1.age << "分数: " << s1.score << endl;

    system("pause");
    return 0;
}

28.结构体数组

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

struct Student {
    string name;
    int age;
    int score;
    };
int main() {
    struct Student stuArray[3] = {
        {"张三",18,100},{"李四",28,99},{"王五",38,66}
    };
    stuArray[2].name = "赵六";
    stuArray[2].age = 80;
    stuArray[2].score = 60;
    for (int i = 0; i < 3; i++) {
        cout << "姓名:" << stuArray[i].name << " 年龄:" << stuArray[i].age << "分数: " << stuArray[i].score << endl;
    }
    system("pause");
    return 0;
}

29.结构体指针

作用:通过指针访问结构体中的成员

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

struct Student {
    string name;
    int age;
    int score;
    };
int main() {
    Student s = { "张三",18,100 };
    Student* p = &s;
    cout << "姓名:" << p->name << " 年龄:" << p->age << "分数: " << p->score << endl;
    
    system("pause");
    return 0;
}

30.结构体做函数参数

作用:将结构体作为参数向函数中传递

#include<iostream>
using namespace std;
#include<string.h>
struct student {
    string name;
    int age;
    int score;
};
void printStudent1(struct student s) {
    cout << "子函数中 姓名:" << s.name << "年龄:" << s.age << "分数:" << s.score << endl;
}
void printStudent2(struct  student *p) {
    cout << "姓名" << p->name << "年龄" << p->age << "分数"<<p->score<<endl;
}
int main() {
    struct student s;
    s.name = "张三";
    s.age = 20;
    s.score = 85;
    cout << "main函数中打印 姓名: " << s.name << "年龄:" << s.age << "分数:" << s.score << endl;
    printStudent1(s);
    printStudent2(&s);
    system("pause");
    return 0;
}

31.内存四区-----程序执行前

C++程序在执行时,将内存大方向划分为4区域

代码区:存放函数体的二进制代码,由操作系统进行管理

全局区:存放全局变量和静态变量以及常量

栈区:由编译器自动分配释放,存放函数的参数值,局部变量等

堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收

在程序编译后,生成了exe可执行程序,未执行该程序前分为两个区域

代码区:存放CPU执行的机器指令

代码区是共享的,是只读的

全局区:

全局变量和静态变量存放在此

全局区还包含了常量区,字符串常量和其他常量也存放在此

该区域的数据在程序结束后由操作系统构成

总结:C++在程序运行前分为全局区和代码区

代码区的特点是共享和只读

全局区中存放全局变量、静态变量、常量

常量区中存放const修饰的全局常量和字符串常量

32.内存四区---程序执行后

栈区数据注意事项---不要返回局部变量的地址

栈区的数据由编译器管理开辟和释放

由程序员分配释放,若程序员不释放,程序结束时由操作系统回收

在C++中主要利用new在堆区开辟内存

33. 引用

数据类型 &别名=原名

int a=10;

int &b=a;

备注:引用必须要初始化;引用一旦初始化后,就不可以更改了;

引用的本质在C++内部实现是一个指针常量

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

void func(int& ref) {
    ref = 100;
}
int main() {
    int a = 100;
    //自动转换为int* const ref=&a;指针常量是指针指向不可改,也说明为什么引用不可更改
    int& ref = a;
    ref = 20;//内部发现ref是引用,自动帮我们转换为 *ref=20
    cout << "a:" << a<< endl;
    cout << "ref: " << ref << endl;
    func(a);
    return 0;
}

34.常量引用

作用:常量引用主要用来修饰形参,防止误操作

在函数形参列表中,可以加const修饰形参,防止形参改变实参

35.函数的默认参数

如果某个位置参数有默认值,那么从这个位置往后,从左向右,必须都要有默认值

如果函数声明有默认值,函数实现的时候就不能有默认参数

占位参数:void func(int a,int){cout<<"this is func"<<endl;}

36.函数重载

作用:函数名可以相同,提高复用性

备注:同一个作用域下;函数名称相同;函数参数类型相同或者个数不同或者顺序不同

注意:引用作为重载条件;函数重载碰到函数默认参数

37.类和对象

//设计一个学生类,属性有姓名和学号,可以给姓名和学号赋值,可以显示学生的姓名和学号
class Student {
public:
    //类中的属性和行为 我们统一称为 成员
    //属性 成员属性 成员变量
    //行为 成员函数 成员方法

    //属性
    string name;
    int id;
    //行为
    void setName(string name) {
        name = name;
    }
    void setId(int id) {
        id = id;
    }
};

38.访问权限

公共权限 public 成员 类内可以访问 类外可以访问

保护权限 protected 成员 类内可以访问 类外不可以访问

私有权限 private 成员 类内可以访问 类外不可以访问

备注:struct默认权限为公共;class默认权限为private

39.成员属性设置为私有

优点1:将所有成员属性设置为私有,可以自己控制读写权限

优点2:对于写权限,我们可以检测数据的有效性

40.构造函数和析构函数

构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用

析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作

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

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

//拷贝构造函数
Person(const Person &p){
     age=p.age;
     cout<<"拷贝构造函数"<<endl;
}
括号法中:调用无参构造函数不能加括号,如果加了编译器认为这是一个函数声明 

41.构造函数调用规则

如果用户定义有参构造函数,C++将不会提供默认的无参构造函数,但是会提供默认拷贝

如果用户定义拷贝构造函数,C++不会再提供其他构造函数

42.深拷贝和浅拷贝

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

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

备注:浅拷贝带来的问题就是堆区的内存重复释放(需要利用深拷贝进行解决)

43.初始化列表

class Person {
public:
    Person(int a, int b, int c) {
        m_a = a;
        m_b = b;
        m_c = c;
    }//传统初始化
    int m_a;
    int m_b;
    int m_c;

    void print() {
        cout << m_a << endl;
        cout << m_b << endl;
        cout << m_c << endl;
    }
};
int main() {
    Person p(1, 2, 3);
    p.print();

    return 0;
}
Person(int a,int b,int c):m_a(a),m_b(b),m_c(c){}

44.类对象作为类成员

C++类中的成员可以是另一个类的对象,我们称该成员为对象成员

当其他类对象作为本类成员,构造时候先构造类对象,再构造自身

class Phone {
public:
    Phone(string pname) {
        m_pname = pname;
    }
    string m_pname;
};
class Person {
public:
    Person(string name, string pname) :m_name(name),m_phone(pname)
    {
    }
    string m_name;
    Phone m_phone;
};
void test01() {
    Person p("张三", "苹果MAX");
    cout << p.m_name << "拿着" << p.m_phone.m_pname<< endl;
}
int main() {
    test01();

    system("pause");
    return 0;
}

45.静态成员

静态成员就是在成员变量和成员函数前加上关键字static,成为静态成员

静态成员变量:所有对象共享同一份数据、在编译阶段分配内存、类内声明,类外初始化

静态成员函数:所有对象共享同一个函数,静态成员函数只能访问静态成员变量

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

class Person {
public:
    static int ma;
    //静态成员变量也具有访问权限
private:
    static int mb;//类外访问不到私有成员变量

};
int Person::ma = 40;//类内声明,类外初始化
int Person::mb = 100;
void test01() {
    Person p;
    cout << p.ma << endl;
    Person p2;
    p2.ma = 200;//共享一份数据
    cout << p.ma << endl;
}
void test02() {
    //静态成员变量 不属于某个对象上 所有对象都共享同一份数据
    //因此静态成员变量有两种访问方式
    //通过对象进行访问
    Person p;
    cout << p.ma << endl;
    //通过类名进行访问
    cout << Person::ma << endl;    
}
int main() {
    test02();
    system("pause");
    return 0;
}
class Person {
public:
    static void func() {
        cout << "static void func调用" << endl;
        m_a = 200;//静态成员函数可以访问 静态成员变量,不可以访问非静态成员变量
    }
    static int m_a;//静态成员变量    
};
void test01() {
    Person p;
    p.func();

    Person::func();//所有对象共享一个函数
}
int main() {    
    system("pause");
    return 0;
}

46.成员变量和成员函数

//成员变量和成员函数分开存储的
class Person {
    int m_a;//非静态成员变量,属于类的对象上
    static int m_b;//静态成员变量,不属于类对象上
    void func() {//非静态成员函数 不属于类的对象上
    }
    static void func2(){}//静态成员函数 不属于类的对象上
};
int Person::m_b = 0;
void test01() {
    Person p;
    //空对象占用内存空间为:1
    //C++编译器会给每一个空对象也分配一个字节空间,是为了区分空对象占内存的位置
    //每个空对象也应该有一个独一无二的内存地址
    cout << "size of p= " << sizeof(p) << endl;
}
void test02() {
    Person p;
    cout << "size of p= " << sizeof(p) << endl;
}
int main() {    
    test01();
    system("pause");
    return 0;
}

47.this指针

每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码

这一块代码是如何区分哪个对象调用自己的呢?

通过使用特殊的对象指针,this指针,this指针指向被调用的成员函数所属的对象

this指针是隐含每一个非静态成员函数内的一种指针

this指针用途:①当形参和成员变量同名时,可用this指针来区分

②在类的非静态成员函数中返回对象本身,可使用return *this.

#include<iostream>
using namespace std;
#include<string.h>
class Person {
public:
    Person(int age) {
        //this指针指向 被调用的成员函数 所属的对象
        this->age = age;
    }
    int age;
    Person& PersonAddAge(Person &p) {
        this->age += p.age;
        //this指向p2的指针,而*this指向的就是p2这个对象本体
        return *this;
    }
};
void test01() {
    Person p1(18);
    cout << "p1的年龄为: " << p1.age << endl;
}
void test02() {
    Person p1(10);
    Person p2(10);
    //链式编程思想
    p2.PersonAddAge(p1).PersonAddAge(p1);
    cout << "p2的年龄为: " << p2.age << endl;
}
int main() {    
    test02();
    system("pause");
    return 0;
}

48.空指针访问成员函数

C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针

class Person {
public:
    void showClassName() {
        cout << "this is Person class" << endl;
    }
    void showPersonAge() {
        if (this == NULL) {
            return;
        }
        //报错原因是因为传入的指针为null
        cout << "age= " << m_age << endl;
    }
    int m_age;
};
void test01() {
    Person* p=NULL;
    p->showClassName();
}
int main() {    
    test01();
    system("pause");
    return 0;
}

49.const修饰成员函数

成员函数后加const后我们称这个函数为常函数

常函数内不可以修饰成员属性

成员属性声明时加关键字mutable后,在常函数中依然可以修改

常对象:声明对象前加const称该对象为常对象;常对象只能调用常函数

class Person {
public:
    //this指针的本质 是指针常量 指针的指向是不可以修改的
    //const Person * const this;
    //在成员函数后面加const,修饰的是this指向,让指针指向的值也不可以修改
    void showPerson() const
    {
        this->m_b = 100;
    }
    int m_a;
    mutable int m_b;//特殊变量,即使在常函数中,也可以修改这个值
};

50.友元

友元的目的:让一个函数或者类 访问另一个类中私有成员

友元关键字为:friend;

友元的三种实现:全局函数做友元;类做友元;成员函数做友元

class Building {
    //goodGay全局函数是Building好朋友,可以访问Building中私有成员
    friend void goodGay(Building* building);
public:
    Building() {
    sitroom = "客厅";
    bedroom = "卧室";
}
public:
    string sitroom;
private:
    string bedroom;
};
void goodGay(Building* building) {
    cout << "好基友" << building->sitroom << endl;
    cout << "好基友" << building->bedroom << endl;
}
void test01() {
    Building building;
    goodGay(&building);
}
class Building;
class GoodGay {
public:
    GoodGay();
    void visit();//参观函数,访问Building中的属性
    Building* building;
};
class Building {
    //GoodGay类是本类的好朋友,可以访问本类中私有成员
    friend class GoodGay;
public:
    Building();
public:
    string sitroom;
private:
    string bedroom;
};
//类外去写成员函数
Building::Building() {
    sitroom = "客厅";
    bedroom = "卧室";
}
GoodGay::GoodGay() {
    building = new Building();
}
void GoodGay::visit() {
    cout << "好基友类正在访问:" << building->sitroom << endl;
    cout << "好基友类正在访问:" << building->bedroom << endl;
}
void test01() {
    GoodGay gg;
    gg.visit();
}
int main() {    
    test01();
    system("pause");
    return 0;

}
class Building;
class GoodGay {
public:
    GoodGay();
    void visit();//让visit函数可以访问Building中私有成员
    void visit2();//让visit函数不可以访问Building中私有成员
    Building* building;
};
class Building {
    //告诉编译器 GoodGay类下的visit成员函数作为本类的好朋友,可以访问私有成员
    friend void GoodGay::visit();
public:
    Building();
public:
    string sitroom;
private:
    string bedroom;
};
//类外去写成员函数
Building::Building() {
    sitroom = "客厅";
    bedroom = "卧室";
}
GoodGay::GoodGay() {
    building = new Building();
}

void test01() {
    GoodGay gg;
    gg.visit();
}
void GoodGay::visit() {
    cout << "visit函数正在访问   " << building->sitroom << endl;
    cout << "visit函数正在访问   " << building->bedroom << endl;
}
void GoodGay::visit2() {
    cout << "visit函数正在访问   " << building->sitroom << endl;
}

int main() {    
    test01();
    system("pause");
    return 0;
}

friend void GoodGay::visit();

51.运算符重载

对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型

//加号运算符重载
class Person {
public:
    /*Person operator+(Person& p) {
        Person temp;
        temp.ma = this->ma + p.ma;
        temp.mb = this->mb + p.mb;
        return temp;
    }*/
    int ma;
    int mb;
};
//全局函数重载+号
Person operator+(Person& p1, Person& p2) {
    Person temp;
    temp.ma = p1.ma + p2.ma;
    temp.mb = p1.mb + p2.mb;
    return temp;
}
void test01() {
    Person p1;
    p1.ma = 10;
    p1.mb = 10;
    Person p2;
    p2.ma = 10;
    p2.mb = 10;
    Person p3 = p1 + p2;
    cout << "p3.ma= " << p3.ma << endl;
    cout << "p3.mb= " << p3.mb << endl;
}
int main() {    
    test01();
    system("pause");
    return 0;
}
//加号运算符重载
class Person {
public:
    /*Person operator+(Person& p) {
        Person temp;
        temp.ma = this->ma + p.ma;
        temp.mb = this->mb + p.mb;
        return temp;
    }*/
    int ma;
    int mb;
};
//全局函数重载+号
Person operator+(Person& p1, Person& p2) {
    Person temp;
    temp.ma = p1.ma + p2.ma;
    temp.mb = p1.mb + p2.mb;
    return temp;
}
void test01() {
    Person p1;
    p1.ma = 10;
    p1.mb = 10;
    Person p2;
    p2.ma = 10;
    p2.mb = 10;
    Person p3 = p1 + p2;
    cout << "p3.ma= " << p3.ma << endl;
    cout << "p3.mb= " << p3.mb << endl;
}
int main() {    
    test01();
    system("pause");
    return 0;
}

51.继承

下级别的成员除了拥有上一级的共性,还有自己的特性

继承的好处:减少重复代码

语法:class 子类:继承方式 父类

子类:派生类 父类:基类

父类中所有非静态成员属性都会被子类继承下去

父类中私有成员属性,是被编译器给隐藏了,因此是访问不到,但是确实被继承下去了

52.继承中构造和析构顺序

先构造父类,再构造字类,析构的顺序和构造的顺序相反

53.继承同名成员处理方式

访问字类同名成员 直接访问即可

class Base {
public:
    Base() {
        ma = 100;
    }
    int ma;
};
class Son :public Base {
public:
    Son() {
        ma = 200;
    }
    int ma;
};
void test01() {
    Son s;
    cout << "ma= " << s.ma << endl;
    //如果通过子类对象,访问到父类中同名成员,需要加作用域
    cout << "Base 下 ma= " << s.Base::ma << endl;
}
int main() {    
    test01();
    system("pause");
    return 0;
}

54.继承同名静态成员处理方式

有两种方式,一是通过对象访问,二是通过类名访问

Son::Base::ma

//第一个::代表通过类名方式访问,第二个::代表访问父类作用域下

备注:多继承语法:class 子类:继承方式 父类1,继承方式 父类2...

55.菱形继承

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

继承前加virtual关键字后,变为虚继承

此时公共的父类Animal称为虚基类

56.多态

静态多态:函数地址早绑定,编译阶段确定函数地址

动态多态:函数地址晚绑定,运行阶段确定函数地址

备注:①有继承关系②子类重写父类的虚函数

动态多态的使用:父类的指针或者引用,指向子类对象

当子类重写父类的虚函数

子类中的虚函数表内部会替换成子类的虚函数地址

57.纯虚函数和抽象类

纯虚函数语法:virtual 返回值类型 函数名(参数列表)=0

抽象类特点:①无法实例化对象;

②子类必须重写抽象类的纯虚函数,否则也属于抽象类

58.虚析构和纯虚析构

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

虚析构和纯虚析构共性:①可以解决父类指针释放子类对象②都需要有具体的函数实现

59.文件操作

程序运行时产生的数据都属于临时数据,程序一旦运行结束都会被释放

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

C++中对文件操作需要包含头文件<fstream>

写文件的步骤:①包含头文件#include<fstream>②创建流对象ofstream ofs

③打开文件 ofs.open("文件路径",打开方式)④写数据ofs<<”写入的数据“

⑤ofs.close()关闭文件

#include<fstream>
void test01() {
    ofstream ofs;
    ofs.open("text.txt", ios::out);
    ofs << "xingming" << endl;
    ofs << "xingbie" << endl;
    ofs.close();
}

60.模板

建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表

template<typename T>
函数声明或定义
void swapInt(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
}
//函数模板
template<typename T>//声明一个模板,告诉编译器后面代码中紧跟着的T不要报错,T是一个通用数据类型
void mySwap(T &a,T &b) {
    T temp = a;
    a = b;
    b = temp;
}
void test01() {
    int a = 10;
    int b = 20;
    mySwap(a, b);//自动类型推导
    mySwap<int>(a, b);//显示指定类型
    cout << "a= " << a << endl;
    cout << "b= " << b << endl;
}

普通函数模板调用时可以发生自动类型转换

函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换

如果利用显示指定类型的方式,可以发生隐式类型转换

int myAdd01(int a, int b) {
    return a + b;
}
void test01() {
    int a = 10;
    int b = 20;
    char c = 'c';//a对应97
    cout << myAdd01(a, c) << endl;
}//隐式类型转换
int myAdd01(int a, int b) {
    return a + b;
}
template<class T>
T myAdd02(T a,T b) {
    return a + b;
}
void test01() {
    int a = 10;
    int b = 20;
    char c = 'c';
    cout << myAdd02(a, c) << endl;
    cout << myAdd02<int> << endl;
}

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

如果函数模板与普通函数都可以调用,优先调用普通函数

可以通过空模板参数列表 强制调用 函数模板

函数模板可以发生函数重载

如果函数模板可以产生更好的匹配,优先调用函数模板

void myPrint(int a,int b) {
    cout << "调用的普通函数" << endl;
}
template<class T>
void myPrint(T a, T b) {
    cout << "调用的模板" << endl;
    myPrint(a, b);
}
void test01() {
    int a = 10;
    int b = 20;
    myPrint<>(a, b);
}

61.模板的局限性

//模板并不是万能的,有些特定数据类型,需要用具体化方式做特殊实现
class Person {
public:
    Person(string name, int age) {
        this->age = age;
        this->name = name;
    }
public:
    string name;
    int age;
};
template<class T>
bool myCompare(T& a, T& b)
{
    if (a == b) {
        return true;
    }
    else
    {
        return false;
    }
}
//利用具体化的Person版本实现代码,具体化优先调用
template<> bool myCompare(Person& p1, Person& p2) {
    if (p1.name == p2.name && p1.age == p2.age) {
        return true;
    }
    else {
        return false;
    }
}
void test01() {
    int a = 10;
    int b = 20;
    bool ret = myCompare(a, b);
    if (ret) {
        cout << "a==b" << endl;
    }
    else {
        cout << "a!=b" << endl;
    }
}
void test02() {
    Person p1("Tom",10);
    Person p2("Tom",10);
    bool ret = myCompare(p1, p2);
    if (ret) {
        cout << "p1==p2" << endl;
    }
    else {
        cout << "p1!=p2" << endl;
    }
}

总结:利用具体化的模板,可以解决自定义类型的通用化

学习模板并不是为了写模板,而是在STL能够运用系统提供的模板

62.类模板

//类模板
template<class NameType,class AgeType>
class Person {
public:
    Person(NameType name, AgeType age) {
        this->age = age;
        this->name = name;
    }
    void showPerson() {
        cout << "name " << this->name << "age " << this->age << endl;
    }
    NameType name;
    AgeType age;
};
void test01() {
    Person<string, int> p1("孙悟空", 99);
    p1.showPerson();
}

63.类模板与函数模板区别

①类模板没有自动类型推导的使用方式

②类模板在模板参数列表中可以有默认参数

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

普通类中的成员函数一开始就可以创建

类模板中的成员函数在调用时才创建

对不同数据类型数组进行排序

//实现通用 对数组进行排序的函数
//规则 从大到小
//算法 选择
//测试char数组 int数组
template<class T>
void mySwap(T& a, T& b) {
    T temp = a;
    a = b;
    b = temp;
}
template<class T>
void mySort(T arr[], int len) {
    for (int i = 0; i < len; i++) {
        int max = i;

        for (int j = i + 1; j < len; j++) {
            if (arr[max] < arr[j]) {
                max = j;
            }
        }
        if (max != i) {
            mySwap(arr[max], arr[i]);
        }
    }
}
//提供打印数组模板
template<class T>
void printArray(T arr[], int len) {
    for (int i = 0; i < len; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
}
void test01() {
    char charArr[] = "badcfe";
    int num = sizeof(charArr) / sizeof(char);
    mySort(charArr, num);
}
class Person1 {
public:
    void showPerson1(){
        cout << "Person1 show" << endl;
    }
};
class Person2 {
public:
    void showPerson2() {
        cout << "Person2 show" << endl;
    }
    
};
template<class T>
class MyClass {
public:
    T obj;
    void func1() {
        obj.showPerson1();
    }
    void func2() {
        obj.showPerson2();
    }
};
void test01() {
    MyClass<Person1>m;
    m.func1();
}//创建时机

65.类模板对象做函数参数

类模板实例化出的对象,向函数传参的方式

67.STL(标准模板库)

STL从广义上分为:容器 算法 迭代器

容器和算法之间通过迭代器进行无缝衔接

六大组件:容器 算法 迭代器 仿函数 适配器(配接器) 空间配置器

STL容器就是将运用的最广泛的一些数据结构实现出来

容器分为序列式容器和关联式容器两种:

序列式容器:强调值的排序

关联式排序:二叉树关系,各元素之间没有严格的物理上的顺序关系

算法分为质变算法和非质变算法

迭代器:提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。每个容器都有自己专属的迭代器

种类:输入迭代器、输出迭代器、前向迭代器、双向迭代器、随机访问迭代器

68.vector容器

算法:for_each

迭代器:vector<int>::iterator

 #include<iostream>
using namespace std;
#include<string.h>
#include<vector>
#include<algorithm>//标准算法头文件
//vector容器存放内置数据类型
void myPrint(int val) {
    cout << val << endl;
}
void test01() {
    //创建了一个vector容器,数组
    vector<int> v;
    //向容器中插入数据
    v.push_back(10);
    v.push_back(20);
    v.push_back(30);
    v.push_back(40);吗
    //通过迭代器访问容器中的数据
    vector<int>::iterator itBegin = v.begin();//起始迭代器,指向容器中第一个元素
    vector<int>::iterator itEnd = v.end();//结束迭代器,指向容器中最后一个元素的下一个位置
    //第一种遍历方式
    while (itBegin != itEnd) {
        cout << *itBegin << endl;
        itBegin++;
    }
    //第二种遍历方式
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
        cout << *it << endl;
    }
    //第三种遍历方式 利用STL提供遍历算法
    for_each(v.begin(), v.end(), myPrint);  
}

69.vector容器存放自定义数据类型

class Person {
public:
    Person(string name, int age) {
        this->age = age;
        this->name = name;
    }
    string name;
    int age;
};
void test01() {
    vector<Person>v;
    Person p1("aaa", 10);
    Person p2("aaa", 20);
    Person p3("aaa", 30);
    Person p4("aaa", 40);
    Person p5("aaa", 50);
    //向容器中添加数据
    v.push_back(p1);
    v.push_back(p2);
    v.push_back(p3);
    v.push_back(p4);
    v.push_back(p5);

    //遍历容器中的数据
    for (vector<Person>::iterator it = v.begin(); it != v.end(); it++) {
        cout << "姓名:"<< (*it).name<<"年龄:"<<(*it).age<< endl;
    }
}
//存放自定义数据类型 指针
void test02() {
    vector<Person*>v;
    Person p1("aaa", 10);
    Person p2("aaa", 20);
    Person p3("aaa", 30);
    Person p4("aaa", 40);
    Person p5("aaa", 50);
    //向容器中添加数据
    v.push_back(&p1);
    v.push_back(&p2);
    v.push_back(&p3);
    v.push_back(&p4);
    v.push_back(&p5);
    //遍历容器
    for (vector<Person*>::iterator it = v.begin(); it != v.end(); it++) {
        cout << "姓名:" << (*it)->name << "年龄:" << (*it)->age << endl;
    }
}

70.Vector容器嵌套容器

//容器嵌套容器,进行遍历输出
void test01() {
    vector<vector<int>>v;
    //创建小容器
    vector<int>v1;
    vector<int>v2;
    vector<int>v3;
    vector<int>v4;
    //向小容器中添加数据
    for (int i = 0; i < 4; i++) {
        v1.push_back(i + 1);
        v2.push_back(i + 2);
        v3.push_back(i + 3);
        v4.push_back(i + 4);
    }
    //将小容器插入到大容器中
    v.push_back(v1);
    v.push_back(v2);
    v.push_back(v3);
    v.push_back(v4);
    //通过大容器,把所有数据遍历一遍
    for (vector<vector<int>>::iterator it = v.begin(); it != v.end(); it++) {
        //(*it)---容器vector<int>
        for (vector<int>::iterator vit = (*it).begin(); vit != (*it).end(); vit++) {
            cout << *vit << " ";
        }
        cout << endl;
    }
}

71.string容器

string本质上是一个类

string和char*的区别:

char *是一个指针;

string是一个类,类内部封装了char*,管理这个字符串,是一个char*型的容器

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

    string str2;
    str2 = str1;
    cout << "str2= " << str2 << endl;

    string str4;
    str4.assign("hello C++");
    cout << "str4= " << str4<< endl;

    string str5;
    str5.assign("hello C++", 5);
    cout << "str5= " << str5 << endl;

    string str6;
    str6.assign(str5);
    cout << "str6= " << str6 << endl;

    string str7;
    str7.assign(10, 'w');
    cout << "str7= " << str7 << endl;

}

72.string字符串拼接

void test01(){
    string str1 = "我";
    str1 += "爱玩游戏";
    cout << "str1= " << str1 << endl;
    str1 += ":";
    cout << "str1= " << str1 << endl;
    string str2 = "lol";
    str1 += str2;
    cout << "str1= " << str1 << endl;

    string str3 = "I";
    str3.append("love");
    cout << "str3= " << str3 << endl;
    str3.append("game abcde",4);
    cout << "str3= " << str3 << endl;

    //str3.append(str2);
    //cout << "str3= " << str3 << endl;
    str3.append(str2,4,3);//从下标4位置开始,截取3个字符,拼接到字符串末尾
    cout << "str3= " << str3 << endl;
}

str3.append(str2,4,3);//从下标4位置开始,截取3个字符,拼接到字符串末尾

70.字符串的查找和替换

void test01() {
    string str1 = "abcdefg";
    int pos = str1.find("df");
    if (pos == -1) {
        cout << "未找到字符串" << endl;
    }
    cout << "pos= " << pos << endl;
    //rfind从右往左找,find从左往右找
    pos = str1.rfind("de");
    cout << "pos= " << pos << endl;
}
void test02() {
    string str1 = "abcdefg";
    //从1号位置起3个字符,替换为“1111”
    str1.replace(1, 3, "1111");
    cout << "str1= " << str1 << endl;
}

备注:找不到返回-1

替换时,要指定从哪个位置起,替换多少参数

71.string字符串比较

void test01() {
    string str1 = "hello";
    string str2 = "hello";
    if (str1.compare(str1) == 0) {
        cout << "str1等于str2" << endl;
    }
    else if (str1.compare(str2) > 0) {
        cout << "str1大于str2" << endl;
    }
    else {
        cout << "str1小于str2" << endl;
    }
}

备注:按照ASCLL码进行对比

=返回0; >返回1; <返回-1

72.字符串存取

//string字符存取
void test01() {
    string str = "hello";
    cout << "str= " << str << endl;
    for (int i = 0; i < str.size(); i++) {
        cout << str[i] << " ";
    }
    cout << endl;
    for (int i = 0; i < str.size(); i++) {
        cout << str.at(i) << " ";
    }
    cout << endl;
    //修改单个字符
    str[0] = 'x';
    cout << "str= " << str << endl;
    str.at(1)='x';
    cout << "str= " << str << endl;
}

73.插入和删除

//字符串的插入和删除
void test01() {
    string str = "hello";
    str.insert(1, "111");
    cout << "str= " << str << endl;
    //删除
    str.erase(1, 3);
    cout << "str= " << str << endl;
}

str.insert(1, "111");

str.erase(1, 3);

备注:插入和删除的起始下标都是从0开始

74.string子串

在字符串中获取想要的子串

//string求子串
void test01() {
    string str = "abcdefg";
    string subStr = str.substr(1, 3);
    cout << "subStr = " << subStr << endl;
}
void test02() {
    string email = "zhangsan@qq.com";
    int pos = email.find("@");
    cout << pos << endl;
    string usrName = email.substr(0, pos);
    cout << usrName << endl;
}

75.vector容器

vector数据结构和数组非常相似,也成为单端数组

不同之处在于数组是静态空间,而vector可以动态扩展

动态扩展:并不是在原空间后虚继新空间,而是找更大的内存空间,然后将原数据拷贝新空间,释放原空间。

//vector容器构造
void printVector(vector<int>& v) {
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;
}
void test01() {
    vector<int>v1;//默认构造 无参构造
    for (int i = 0; i < 10; i++) {
        v1.push_back(i);
    }
    printVector(v1);
    //通过区间进行构造
    vector<int>v2(v1.begin(), v1.end());
    printVector(v2);
    //n个elem方式构造
    vector<int>v3(10, 100);
    printVector(v3);
    //拷贝构造
    vector<int>v4(v3);
    printVector(v4);
}

76.vector赋值操作

void printVector(vector<int>& v) {
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;
}
void test01() {
    vector<int>v1;
    for (int i = 0; i < 10; i++) {
        v1.push_back(i);
    }
    printVector(v1);
    //赋值
    vector<int>v2;
    v2 = v1;
    printVector(v2);

    vector<int>v3;
    v3.assign(v1.begin(), v1.end());
    printVector(v3);

    //n个elem赋值
    vector<int>v4;
    v4.assign(10, 100);
    printVector(v4);
}

77.vector的容量和大小

void printVector(vector<int>& v) {
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}
void test01() {
    vector<int>v1;
    for (int i = 0; i < 10; i++) {
        v1.push_back(i);
    }
    printVector(v1);
    if (v1.empty()) //为真 代表容器为空
    {
        cout << "v1不为空" << endl;
    }
    else {
        cout << "v1不为空" << endl;
        cout << "v1的容量: " << v1.capacity() << endl;
        cout << "v1的大小: " << v1.size() << endl;
    }
    //重新指定大小
    v1.resize(15);//利用重载版本,可以指定默认填充值,参数2
    printVector(v1);//如果重新指定的比原来长了,默认用填充新的位置
    v1.resize(5);
    printVector(v1);//如果重新指定的比原来短了,超出部分会删除掉
}

78.vector数据存取

//vector容器 数据存取
void test01() {
    vector<int>v1;
    for (int i = 0; i < 10; i++) {
        v1.push_back(i);
    }
    //利用[]方式访问数组元素
    for (int i = 0; i < v1.size(); i++) {
        cout << v1[i] << " ";
    }
    cout << endl;
    //利用at方式访问元素
    for (int i = 0; i < v1.size(); i++) {
        cout << v1.at(i) << " ";
    }
    cout << endl;

    //返回第一个元素
    cout << "第一个元素: " << v1.front() << endl;
    //获取最后一个元素
    cout << "最后一个元素为: " << v1.back() << endl;
}

79.vector互换容器

实现两个容器内元素进行互换

//vector容器互换
void printVector(vector<int>& v) {
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;
}
void test01() {
    vector<int>v1;
    for (int i = 0; i < 10; i++) {
        v1.push_back(i);
    }
    cout << "交换前: " << endl;
    printVector(v1);
    vector<int>v2;
    for (int i = 10; i > 0; i--) {
        v2.push_back(i);
    }
    printVector(v2);
    cout << "交换后: " << endl;
    v1.swap(v2);
    printVector(v1);
    printVector(v2);
}
//实际用途
//巧用swap可以收缩内存空间
void test02() {
    vector<int>v;
    for (int i = 0; i < 10000; i++) {
        v.push_back(i);
    }
    cout << "v的容量为:" << v.capacity() << endl;
    cout << "v的大小: " << v.size() << endl;
    
    vector<int>(v).swap(v);
    cout << "v的容量为:" << v.capacity() << endl;
    cout << "v的大小: " << v.size() << endl;
}

80.vector预留空间

//vector预留空间
void test01() {
    vector<int>v;
    //利用reserve(100000)
    v.reserve(100000);
    int num = 0;//统计开辟次数
    int* p = NULL;
    for (int i = 0; i < 100000; i++) {
        v.push_back(i);
        if (p != &v[0]) {
            p = &v[0];
            num++;
        }
    }
    cout << "num= " << num << endl;
}

如果数据量较大,可以一开始利用reserve预留空间

81.deque容器

双端数组,可以对头端进行插入删除操作

deque和vector区别:

vector对于头部的插入删除效率低,数据量越大,效率越低

deque相对而言,对头部的插入删除速度会比vector快

vector访问元素的速度会比deque快,这和两者内部实现有关。

deque内部工作原理:deque内部有个中控器,维护每段缓冲区中的内容,缓冲区中存放真实数据

中控器维护的是每个缓冲区的地址,使得使用deque时像一片连续的内存空间

deque容器的迭代器也是支持随机访问的

82.deque插入和删除

void printDeque(const deque<int>& d) {
    for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;
}
void test01() {
    deque<int>d1;
    //尾插
    d1.push_back(10);
    d1.push_back(20);
    //头插
    d1.push_front(100);
    d1.push_front(200);
    printDeque(d1);
    //尾删
    d1.pop_back();
    printDeque(d1);
    //头删
    d1.pop_front();
    printDeque(d1);
}
void test02() {
    deque<int>d1;
    d1.push_back(10);
    d1.push_back(20);
    d1.push_front(100);
    d1.push_front(200);
    printDeque(d1);
    //insert插入
    d1.insert(d1.begin(), 2, 10000);
    printDeque(d1);
    //10000 10000 200 100 10 20
    deque<int>d2;
    d2.push_back(1);
    d2.push_back(2);
    d2.push_back(3);
    d1.insert(d1.begin(), d2.begin(), d2.end());
    printDeque(d1);
    //1 2 3 10000 10000 200 100 10 20
}
void test03() {
    deque<int>d1;
    d1.push_back(10);
    d1.push_back(20);
    d1.push_front(100);
    d1.push_front(200);
    //删除
    deque<int>::iterator it = d1.begin();
    it++;
    d1.erase(it);
    printDeque(d1);
    //按区间方式删除
    d1.erase(d1.begin(), d1.end());
}

83.deque数据存取

//deque容器排序
void printDeque(const deque<int>&d) {
    for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;
}
void test01() {
    deque<int>d;
    d.push_back(10);
    d.push_back(20);
    d.push_back(30);
    d.push_front(100);
    d.push_front(200);
    d.push_front(300);
    printDeque(d);
    sort(d.begin(), d.end());
    cout << "排序后:" << endl;
    printDeque(d);
}

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

84.stack容器

栈不允许有遍历行为

void test01() {
    stack<int>s;
    s.push(10);
    s.push(20);
    s.push(30);
    s.push(40);
    while (!s.empty())
    {
        cout << "栈顶元素为:" << s.top() << endl;
        s.pop();
    }
    cout << "栈的大小:" << s.size() << endl;
}

85.queue容器

队列中只有队头和队尾才可以被外界使用,因此队列不允许有遍历行为

86.list容器

由于链表的存储方式并不是连续的内存空间,因此链表list中的迭代器只支持前移和后移,属于双向迭代器。

list的优点:

采用动态存储分配,不会造成内存浪费和溢出

链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素

list的缺点:

链表灵活,但是空间(指针域)和时间(遍历)额外耗费较大

备注:list有一个重要的性质,插入操作和删除操作都不会造成原有list迭代器的失效,这在vector是不成立的。

void printList(const list<int>& L) {
    for (list<int>::const_iterator it = L.begin(); it != L.end();it++) {
        cout << *it << " ";
    }
    cout << endl;
}
void test01() {
    //创建list容器
    list<int>L1;
    //添加数据
    L1.push_back(10);
    L1.push_back(20);
    L1.push_back(30);
    L1.push_back(40);
    //遍历容器
    printList(L1);
    //区间方式构造
    list<int>L2(L1.begin(), L1.end());
    printList(L1);
    //n个elem
    list<int>L4(10, 1000);
    printList(L4);
}

87.list赋值和交换

给list容器进行赋值,以及交换list容器

push_back(elem):在容器尾部加入一个元素

pop_back():删除容器中最后一个元素

push_front(elem):在容器开头插入一个元素

pop_front():从容器开头移除第一个元素

insert(pos,elem):在pos位置插elem元素的拷贝,返回新数据的位置

insert(pos,n,elem):在pos位置插入n个elem数据,无返回值

inset(pos,beg,end):在pos位置插入[beg,end]区间的数据,无返回值

clear():移除容器的所有数据

erase():删除[beg,end]区间的数据,返回下一个数据的位置

remove(elem):删除容器中所有与elem值匹配的元素

88.list数据存取

对list容器中数据进行存取

//list容器的数据存取
void test01() {
    list<int>L1;
    L1.push_back(10);
    L1.push_back(20);
    L1.push_back(30);
    L1.push_back(40);
    //L1[0] 不可以用[]访问list容器中的元素
    //L1.at(0) 不可以用at方式访问list容器中的元素
    //原因是list本质链表,不是用连续线性空间存储数据,迭代器也是不支持随机访问的
    cout << "第一个元素为:" << L1.front() << endl;
    cout << "最后一个元素为:" << L1.back() << endl;
    //验证迭代器是不支持随机访问的
    list<int>::iterator it = L1.begin();
    it++;//不可以it=it+1;支持双向,不支持随机访问
}

89.排序案例

//按照年龄进行升序,如果年龄相同按照身高进行降序
class Person {
public:
    Person(string name, int age, int height) {
        this->name = name;
        this->age = age;
        this->height = height;
    }
    string name;
    int age;
    int height;
};
bool comparePerson(Person& p1, Person& p2) {
    //按照年龄 升序
    if (p1.age == p2.age) {
        //年龄相同,按照身高降序
        return p1.height > p2.height;
    }
    else {
        return p1.age < p2.age;
    }
}
void test01() {
    list<Person>L;
    Person p1("刘备", 35, 175);
    Person p2("曹操", 45, 180);
    Person p3("孙权", 45, 170);
    Person p4("赵云", 25, 190);
    Person p5("张飞", 35, 160);
    Person p6("关羽", 35, 200);

    L.push_back(p1);
    L.push_back(p2);
    L.push_back(p3);
    L.push_back(p4);
    L.push_back(p5);
    L.push_back(p5);
    for (list<Person>::iterator it = L.begin(); it != L.end(); it++) {
        cout << "姓名: " << (*it).name << "年龄:" << (*it).age << "身高: " << (*it).height << endl;
    }
    cout << "排序后" << endl;
    L.sort(comparePerson);
    for (list<Person>::iterator it = L.begin(); it != L.end(); it++) {
        cout << "姓名: " << (*it).name << "年龄:" << (*it).age << "身高: " << (*it).height << endl;
    }
}//指定排序规则

90.set容器和multiset容器

所有元素都会在插入时自动被排序

set不允许容器中有重复的元素

multiset允许容器中有重复的元素

//set容器构造和赋值
void printSet(set<int>& s) {
    for (set<int>::iterator it = s.begin(); it != s.end(); it++) {
        cout << *it <<" ";
    }
    cout << endl;
}
void test01() {
    set<int>s1;
    //插入数据,只有inset方式
    s1.insert(10);
    s1.insert(30);
    s1.insert(20);
    s1.insert(30);
    s1.insert(40);
    //遍历容器
    //set容器特点:所有元素插入时候自动被排序
    //set容器不允许插入重复值
    printSet(s1);
      //拷贝构造
    set<int>s2(s1);
    printSet(s2);
    //赋值
    set<int>s3;
    s3 = s2;
    printSet(s3);
}

备注:s1.erase(s1.begin());

s1.erase(30);

s1.erase(s1.begin(),s1.end());

//set容器 查找和统计
void test01() {
    set<int>s1;
    s1.insert(10);
    s1.insert(30);
    s1.insert(20);
    s1.insert(40);
    set<int>::iterator pos = s1.find(30);
    if (pos != s1.end()) {
        cout << "找到元素:" << *pos << endl;
    }
    else {
        cout << "未找到元素:" << endl;
    }
}
void tese02() {
    set<int>s1;
    s1.insert(10);
    s1.insert(30);
    s1.insert(20);
    s1.insert(40);
    //统计30的个数
    int num = s1.count(30);
    //对于set容器而言,结果要么是0,要么是1
    cout << "num= " << num << endl;
}

91.set和multiset区别

set不可以插入重复数据,而multiset可以

set插入数据的同时会返回插入结果,表示插入是否成功

multiset不会检测数据,因此可以插入重复数据

//set容器 和multiset容器的区别
void test01() {
    set<int>s;
    pair<set<int>::iterator,bool> rest=s.insert(10);
    if (rest.second) {
        cout << "第一次插入成功" << endl;
    }
    else {
        cout << "第一次插入失败" << endl;
    }
    
    rest = s.insert(10);
    if (rest.second) {
        cout << "第一次插入成功" << endl;
    }
    else {
        cout << "第一次插入失败" << endl;
    }
}

92.pair对组创建

成对出现的数据,利用对组可以返回两个数据

//pair对组的创建
void test01() {
    pair<string, int>p("Tom", 20);
    cout << "姓名:" << p.first << "年龄:" << p.first << endl;
    //第二种方式
    pair<string, int>p2 = make_pair("Ierry", 30);
    cout << "姓名:" << p.first << "年龄:" << p.first << endl;
}

93.set容器排序

//set容器排序
class MyCompare{
public:
    bool operator()(int v1,int v2){
        return v1 > v2;
}
};
void test01() {
    set<int>s1;
    s1.insert(10);
    s1.insert(20);
    s1.insert(30);
    s1.insert(40);
    s1.insert(50);
    for (set<int>::iterator it = s1.begin(); it!= s1.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;
    //指定排序规则为从大到小
    set<int, MyCompare>s2;
    s2.insert(10);
    s2.insert(40);
    s2.insert(20);
    s2.insert(30);
    s2.insert(50);
    for (set<int, MyCompare>::iterator it = s2.begin(); it != s2.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;
}

94.set容器排序

//set容器排序,存放自定义数据类型
class Person{
public:
    Person(string name, int age) {
        this->age = age;
        this->name = name;
    }
    string name;
    int age;
};
class comparePerson{
public:
    bool operator()(const Person& p1,const Person& p2) {
        return p1.age > p2.age;
    }
};
void test01() {
    set<Person,comparePerson> s;
    //创建Person对象
    Person p1("刘备", 24);
    Person p2("关羽", 22);
    Person p3("张飞", 23);
    Person p4("赵云", 24);
    s.insert(p1);
    s.insert(p2);
    s.insert(p3);
    s.insert(p4);
    for (set<Person,comparePerson>::iterator it = s.begin(); it!= s.end(); it++) {
        cout<<"姓名:" <<it->name<<"年龄:"<<it->age<< endl;
    }
}

95.map/multimao容器

map中所有元素都是pair;

pair中第一个元素为key键值,起到索引作用,第二个元素为value实值

所有元素都会根据元素的键值自动排序

两者区别,是否允许容器中有重复的key值元素

//map容器 构造和赋值
void printMap(map<int, int>& m) {
    for (map<int, int>::iterator it = m.begin(); it != m.end(); it++) {
        cout << "key= " << (*it).first << "value= " << (*it).second << endl;
    }
    cout << endl;
}
void test01() {
    map<int,int>m;
    m.insert(pair<int, int>(1, 10));
    m.insert(pair<int, int>(2, 20));
    m.insert(pair<int, int>(3, 30));
    m.insert(pair<int, int>(4, 40));

    printMap(m);
    //拷贝构造
    map<int, int>m2(m);
    printMap(m2);
    //赋值
    map<int, int>m3;
    m3 = m2;
    printMap(m3);
}

备注:map中所有元素都是成对出现,插入数据时候要使用对组

96.map插入和删除

//map容器插入和删除
void printMap(map<int, int>& m) {
    for (map<int, int>::iterator it = m.begin(); it != m.end(); it++) {
        cout << "key= " << it->first << "value= " << it->second << endl;
    }
}
void test01() {
    map<int, int>m;
    //插入
    //第一种
    m.insert(pair<int, int>(1, 10));
    //第二种
    m.insert(make_pair(2, 20));
    //第三种
    m.insert(map<int, int>::value_type(3, 30));
    //第四种
    m[4] = 40;
    cout << m[5] << endl;
    printMap(m);
    //删除
    m.erase(m.begin());
    printMap(m);
    m.erase(3);//按照key删除
    printMap(m);
    m.erase(m.begin(), m.end());
    printMap(m);
}

map<int,int>::iterator pos=m.find(3);

map不允许插入重复key元素,count统计而言,结果要么是0,要么是1;

multimap的count的统计可能大于1

97.map容器排序

map容器默认排序规则为 按照key值进行 从小到大排序,掌握如何改变排序规则

利用仿函数,可以改变排序规则

//map容器排序
class MyCompare {
public:
    bool operator()(int v1, int v2) {
        //降序
        return v1 > v2;
    }
};
void test01() {
    map<int, int,MyCompare>m;
    m.insert(pair<int, int>(1, 10));
    m.insert(pair<int, int>(2, 20));
    m.insert(pair<int, int>(3, 30));
    m.insert(pair<int, int>(4, 40));
    m.insert(pair<int, int>(5, 50));
    for (map<int, int,MyCompare>::iterator it = m.begin(); it != m.end(); it++) {
        cout << "key= " << it->first << "value= " << it->second << endl;
    }
}

98.员工分组

//创建10名员工,放到vector中
//遍历vector容器,取出每个员工,进行随机分组
//分组后,将员工部门编号作为key,具体员工工作为value,放入到multimap容器中
//分部门现实员工信息
#define CEHUA 0
#define MEISHU 1
#define YANFA 2
class Worker {
public:
    string name;
    int salary;
};

void createWorker(vector<Worker>& v) {
    string nameSeed = "ABCDEFGHIJ";
    for (int i = 0; i < 10; i++) {
        Worker worker;
        worker.name = "员工";
        worker.name += nameSeed[i];

        worker.salary = rand() % 10000 + 10000;
        //将员工放入到容器中
        v.push_back(worker);
    }
}

void setGroup(vector<Worker>& v, multimap<int, Worker>&m) {
    for (vector<Worker>::iterator it = v.begin(); it != v.end(); it++) {
        //产生随机部门编号
        int deptId = rand() % 3;
        //key部门编号,value具体员工
        m.insert(make_pair(deptId, *it));
    }
}

oid showWorkerByGroup(multimap<int, Worker>&m) {
    cout << "策划部门:" << endl;
    multimap<int,Worker>::iterator pos=m.find(CEHUA);
    int count = m.count(CEHUA);//统计具体人数
    int index = 0;
    for (; pos != m.end()&&index<count; pos++,index++) {
        cout << "姓名:" << pos->second.name << "工资: " << pos->second.salary << endl;
    }
}

int main() {    
    srand((unsigned int)time(NULL));
    //创建员工
    vector<Worker>vWorker;
    createWorker(vWorker);
    
    //员工分组
    multimap<int, Worker>mWorker;
    setGroup(vWorker, mWorker);
    //分组显示员工
    showWorkerByGroup(mWorker);
    system("pause");
    return 0;
}

99.函数对象

概念:重载函数调用操作符的类,其对象常称为函数对象;函数对象使用重载的()时,行为类似函数调用,也叫仿函数

本质:函数对象(仿函数)是一个类,不是一个函数

特点:函数对象在使用时,可以像普通函数那样调用,可以有参数,可以有返回值

函数对象超出普通函数的概念,函数对象可以有自己的状态

函数对象可以作为参数传递

class MyAdd {
public:
    int operator()(int v1, int v2) {
        return v1 + v2;
    }
};
//函数对象在使用时,可以像普通函数那样调用,可以有参数,可以有返回值
void test01() {
    MyAdd myAdd;
    cout << myAdd(10, 10) << endl;
}
//函数对象超出普通函数的概念,函数对象可以有自己的状态
class MyPrint {
public:
    MyPrint() {
        this->count = 0;
    }
    void operator()(string text) {
        cout << text << endl;
        this->count++;
    }
    int count;//内部自己状态
};
void test02() {
    MyPrint myPrint;
    myPrint("hello world");
    myPrint("hello world");
    myPrint("hello world");
    cout << "myPrint调用次数为: " << myPrint.count << endl;
}
//函数对象可以作为参数传递
void doPrint(MyPrint&mp,string test) {
    mp(test);
}
void test03() {
    MyPrint myPrint;
    doPrint(myPrint, "Hello c++");
}

100.谓词

返回bool类型的仿函数成为谓词

如果operator()接受一个参数,那么叫做一元谓词

如果operator()接受两个参数,那么叫做二元谓词

class GreaterFive {
public:
    bool operator()(int val) {
        return val > 5;
    }
};
void test01() {
    vector<int>v;
    for (int i = 0; i < 10; i++) {
        v.push_back(i);
    }
    //查找容器中有没有大于5的数字
    vector<int>::iterator it=find_if(v.begin(), v.end(), GreaterFive());
    if (it == v.end()) {
        cout << "未找到" << endl;
    }
    else {
        cout << "找到了大于5 的数字为: " << *it << endl;
    }
}
class MyCompare {
public:
    bool operator()(int val1,int val2) {
        return val1 > val2;
    }
};
void test01() {
    vector<int>v;
    v.push_back(10);
    v.push_back(40);
    v.push_back(20);
    v.push_back(30);
    v.push_back(50);
    sort(v.begin(), v.end());
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
        cout << *it << endl;
    }
    cout << endl;
    //使用函数对象,改变算法策略,变为排序规则为从大到小
    sort(v.begin(), v.end(), MyCompare());
    cout << "---------" << endl;
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
        cout << *it << endl;
    }
    cout << endl;
}

101.内建函数对象

算术仿函数、关系仿函数、逻辑仿函数

#include<functional>

//内建函数对象 算术仿函数
//negate 一元仿函数  取反仿函数
void test01() {
    negate<int>n;
    cout << n(50) << endl;
}
void test02() {
    plus<int>p;
    cout << p(10, 20) << endl;
}
//内建函数对象 关系仿函数
//大于 greater
void test01() {
    vector<int>v;
    v.push_back(10);
    v.push_back(30);
    v.push_back(40);
    v.push_back(20);
    v.push_back(50);
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
        cout << *it << endl;
    }
    cout << endl;
    //降序
    sort(v.begin(), v.end(), greater<int>());
}
//内建函数对象 逻辑仿函数
//大于 greater
void test01() {
    vector<bool>v;
    v.push_back(true);
    v.push_back(false);
    v.push_back(true);
    v.push_back(true);
    for (vector<bool>::iterator it = v.begin(); it != v.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;
    //利用逻辑非 将容器v搬运到容器v2中,并执行取反操作
    vector<bool>v2;
    v2.resize(v.size());
    transform(v.begin(), v.end(), v2.begin(), logical_not<bool>());
    for (vector<bool>::iterator it = v2.begin(); it != v2.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;
}

102.STL常用算法

算法主要是由头文件<algorithm>.<functional>.<numeric>

//常用遍历算法 for_each
//普通函数
void print01(int val) {
    cout << val << " ";
}
//仿函数
class print02 {
public:
    void operator()(int val) {
        cout << val << " ";
    }
};
void test01() {
    vector<int>v;
    for (int i = 0; i < 10; i++) {
        v.push_back(i);
    }
    for_each(v.begin(), v.end(), print01);
    cout << endl;
    for_each(v.begin(), v.end(), print02());
}

transform(iterator beg1,iterator end1,iterator beg2,_func);

// transform
class Transform {
public:
    int operator()(int v) {
        return v;
    }
};
class MyPrint {
public:
    void operator()(int val) {
        cout << val << " ";
    }
};
void test01() {
    vector<int>v;
    for (int i = 0; i < 10; i++) {
        v.push_back(i);
    }
    vector<int>vTarget;//目标容器
    vTarget.resize(v.size());//目标容器,需要提前开辟空间
    transform(v.begin(), v.end(), vTarget.begin(), Transform());
    for_each(vTarget.begin(),vTarget.end(),MyPrint());
    cout << endl;
}

103.查找算法

void test01() {
    vector<int>v;
    for (int i = 0; i <10; i++) {
        v.push_back(i);
    }
    //查找容器中 是否有5这个元素
    vector<int>::iterator it=find(v.begin(), v.end(), 5);
    if (it == v.end()) {
        cout << "没有找到" << endl;
    }
    else {
        cout << "找到:" << *it << endl;
    }
}
class Person {
public:
    Person(string name, int age) {
        this->mname = name;
        this->mage = age;
    }
    bool operator==(const Person& p) {
        if (this->mname == p.mname && this->mage == p.mage) {
            return true;
        }
        else {
            return false;
        }
    }
    string mname;
    int mage;
};
void test02() {
    vector<Person>v;
    //创建数据
    Person p1("aaa", 10);
    Person p2("bbb", 20);
    Person p3("ccc", 30);
    Person p4("ddd", 40);
    //放入到容器中
    v.push_back(p1);
    v.push_back(p2);
    v.push_back(p3);
    v.push_back(p4);
    vector<Person>::iterator it=find(v.begin(), v.end(), p2);
    if (it == v.end()) {
        cout << "没有找到" << endl;
    }
    else {
        cout << "找到元素 姓名:" << it->mname << "年龄:" << it->mage << endl;
    }
}

备注:利用find可以在容器中找指定的元素,返回值是迭代器

//常用查找算法 find_if
//查找内置数据类型
class GreaterFive {
public:
    bool operator()(int val) {
        return val > 5;
    }
};
void test01() {
    vector<int>v;
    for (int i = 0; i < 10; i++) {
        v.push_back(i);
    }
    vector<int>::iterator it=find_if(v.begin(), v.end(), GreaterFive());
    if (it == v.end()) {
        cout << "没有找到" << endl;
    }
    else {
        cout << "找到了大于5的数字:" << *it << endl;
    }
}
//自定义数据类型
class Person {
public:
    Person(string name,int age) {
        this->mname = name;
        this->mage = age;
    }
    string mname;
    int mage;
};
class Greater {
public:
    bool operator()(Person& p) {
        return p.mage > 20;
    }
};
void test02() {
    vector<Person>v;
    Person p1("aaa", 10);
    Person p2("bbb", 20);
    Person p3("ccc", 30);
    Person p4("ddd", 40);
    v.push_back(p1);
    v.push_back(p2);
    v.push_back(p3);
    v.push_back(p4);

    vector<Person>::iterator it=find_if(v.begin(), v.end(),Greater());
    if (it == v.end()) {
        cout << "没有找到" << endl;
    }
    else {
        cout << "找到姓名:" << it->mname << "年龄:" << it->mage << endl;
    }
}

备注:adjacent_find查找相邻重复元素

//查找指定元素是否存在 binary_search
void test01() {
    vector<int>v;
    for (int i = 0; i < 10; i++) {
        v.push_back(i);
    }
    //查找容器中是否有9元素
    //注意:容器必须是有序的序列
    bool ret = binary_search(v.begin(), v.end(), 9);
    if (ret) {
        cout << "找到了元素" << endl;
    }
    else {
        cout << "未找到" << endl;
    }
}

备注:二分查找法查找效率很高,值得注意的是查找的容器中元素必须是有序序列

104.常用排序算法

//常用排序算法 random_shuffle 
//统计内置数据类型
void myPrint(int val) {
    cout << val << " ";
}
void test01() {
    srand((unsigned int)time(NULL));
    vector<int>v;
    for (int i = 0; i < 10; i++) {
        v.push_back(i);
    }
    //利用洗牌 算法 打乱顺序
    random_shuffle(v.begin(), v.end());
    for_each(v.begin(), v.end(),myPrint);
    cout << endl;
}

merge:两个容器元素合并,并存储到另一容器中

容器元素合并,并存储到另一容器中

两个容器必须是有序的

//常用排序算法 merge 必须是有序序列
//统计内置数据类型
class myPrint {
public:
    void operator()(int val) {
        cout << val << " ";
    }
};
void test01() {
    vector<int>v1;
    vector<int>v2;
    for (int i = 0; i < 10; i++) {
        v1.push_back(i);
        v2.push_back(i + 1);
}
    //目标容器
    vector<int>vTarget;
    //提前给目标容器分配空间
    vTarget.resize(v1.size() + v2.size());
    merge(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin());
    for_each(vTarget.begin(), vTarget.end(), myPrint());
    cout << endl;
}

105.常用拷贝和替换算法

//常用拷贝和替换算法 copy
void myPrint(int val) {
    cout << val << " ";
}
void test01() {
    vector<int>v;
    for (int i = 0; i < 10; i++) {
        v.push_back(i);
    }
    vector<int>v2;
    v2.resize(v.size());
    copy(v.begin(), v.end(), v2.begin());
    for_each(v2.begin(), v2.end(), myPrint);
    cout << endl;
}

备注:利用copy算法在拷贝时,目标容器记得提前开辟空间

replace(v.begin(),v.end(),20,2000)

replace_if(v.begin(),v.end(),Greater30(),3000)

按条件查找,可以利用仿函数灵活筛选满足的条件

swap(v1,v2)交换的容器要同种类型

106.常用算术生成算法

头文件#include<numeric>

accumulate:计算容器元素累计总和

fill:向容器中添加元素

void test01() {
    vector<int>v;
    for (int i = 0; i < 100; i++) {
        v.push_back(i);
    }
    int total=accumulate(v.begin(), v.end(), 0);
    cout << "total= " << total << endl;
}
//常用算术生成算法 fill
void myPrint(int val) {
    cout << val << endl;
}
void test01() {
    vector<int>v;
    v.resize(10);
    //后期重新填充
    fill(v.begin(),v.end(),100);
    for_each(v.begin(), v.end(), myPrint);
}

107.常用集合算法

set_intersection:交集

set_union:求两个集合的并集(两个集合必须是有序序列;set_union返回值既是并集中最后一个元素的位置)

//常用集合算法 set_difference
void myPrint(int val) {
    cout << val << " ";
}
void test01() {
    vector<int>v1;
    vector<int>v2;
    for (int i = 0; i < 10; i++) {
        v1.push_back(i);
        v2.push_back(i + 5);
    }
    //创建目标容器
    vector<int>vTarget;
    //给目标容器开辟空间
    //最特殊的情况,两个容器没有交集,取两个容器中大的size作为目标容器开辟空间
    vTarget.resize(max(v1.size(),v2.size()));
    cout << "v1和v2的差集为:" << endl;
    vector<int>::iterator itEnd=set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin());
    for_each(vTarget.begin(), itEnd, myPrint);
    cout << endl;
}  

注意:要注意谁跟谁的差集,用itEnd,不然会多输出几个值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值