vs注释快捷键

添加注释快捷键:ctrl + k· + c

取出注释快捷键:ctrl + k + u

代码补全快捷键:ctrl + j

数据的输出:cout

cout << "a = " << a << endl;	//加了<< endl 表示换行,不加就不换行

定义常量

1、#define i 10
2、const int i = 10;//常量不可被修改

sizeof 统计数据类型所占的内存大小

sizeof (数据类型/变量)

整型

short		2字节
int 4字节
long 4字节
long long 8字节

浮点型

double (不用加表示)		8字节
float f1 = 3.14f(加f) 4字节

字符串型

1.char str[] = "Hello World"
2.string str = "Hello World" 使用时要#include <string>
string类型可以装中文

布尔型

bool	1字节
布尔类型只要非0,都代表真

科学计数法

e后为正	3e2 : 	3 * (10 ^ 2)
e后为负 3e-2: 3 * (0.1 ^2)

转义字符

\n:换行
\t:跳到下一个tab位置(可以对齐\t后面的字符)
\\:代表一个“\”
\?:代表一个“?”

数据的输入:cin

cin >> a

跳转语句:

break	跳出当前循环,并终止循环
contrue 结束此次循环,继续下一次循环
goto 无条件跳转语句
goto使用: goto FLAG;(前往标记的那一行,FLAG可以随便取)
FLAG:(冒号表示这是个标记)

头文件:

#include "hello.h"	用双引号,表示是自己自定义的头文件
#include <iostream>
using namespace std; 这两句写在头文件中,源文件中只要包含“Hello.h”就行了

c++里不用写#ifndef #define #endif 并不会报警告
并且头文件中不用再包含自身的头文件

指针

定义: int * a	32位系统4字节
64位系统8字节(在上方的解决方案列表中可以修改系统位数)
指针前加一个*,代表解引用,指向指针所指向的内存

空指针指向的内存是不允许进行操作的:int * p = NULL 不能直接访问 *p

野指针:

int * p =  (int *)0x1100 	指针变量指向非法的内存空间

常量指针:

const int * p	(const修饰的是*p,所以指向的值不能修改,但可以直接对内存进行修改,例直接改a:a = 20)

指针常量:

int * const p	(const指向的是指针变量,所以p的指向不能修改)
指针的指向不可以改,但指向的值可以修改

int * const p = &a; *p = 20;(true) p = &b;(error)

指针指向数组:

int * p = arr;	数组的名字就是数组的首地址

结构体:

定义	:
struct st {
string name;
int age;
};

创建: struct st st1 = {"zhangsan",18};

结构体数组:struct st starr[2] = {
{"zhangsan",20},
{"lisi",10}
};
结构体数组的访问:starr[1].name = "wangwu";

结构体指针:要用->
struct st st1 = {"zhangsan",20};

struct st * p = &st1;通过指针指向结构体
访问:p->name = "zhaoliu"; 用->来访问成员,p前面不用加*,直接用指针指向成员就行了

const修饰结构体:const struct st st1 防止误操作

system命令:

system("pause"):	按任意键继续
system("cls"); 清屏

四个分区:

代码区:	代码区是共享的(只存在一份文件,而多个进程共享使用)
代码区是只读的(防止程序被意外修改)
全局区: 存放全局变量、静态变量以及常量
该区域的数据在结束后有操作系统释放
静态变量:static int i = 10 在局部变量前加static
常量:分为字符串常量和const修饰的变量
const修饰的变量: const修饰的全局变量
const修饰的局部变量(不在全局区)
栈区: 由编译器管理开辟和释放(指针也是局部变量,存放在栈区)
局部变量存放在栈区,不要返回局部变量的地址(因为语句执行完后,局部变量对应内存会被编译器自动释放)
堆区: 由程序员分配释放,若不释放,程序结束后有操作系统回收
用new可以在堆区开辟内存,释放用delete
int * p = new int(10);(会返回指针,则用指针变量来接收)

new关键字:

int * p = new int(10);	新建一个对象
delete p; 释放该堆区的内存
int * arr = new int[10] new开辟数组

可以直接arr[]加下标使用数据
delete[] arr; 释放数组要加上[]

引用:

作用:给变量取别名
本质:引用本质是一个指针常量
语法:数据类型 &别名 = 原名
例: int a = 10;
int &b = a; 给a取了个别名叫b,修改时a与b修改的是同一块内存
注意事项:1、引用必须初始化(例int &b错误,要初始化)
2、引用在初始化后就不能改变了
错误: int &b = a;int &b = c;不能指向a后再改变为指向c;
引用作为函数形参:
例: void exchange3(int &a, int &b) {
int temp;
temp = a;
a = b;
b = temp;
}
调用exchange3(a,b)即可以互换a,b的值,因为形参定义的引用分别指向了a与b,互换引用即可互换对应内存的值
引用作为函数返回值:
1、不要返回局部变量的引用(加上static就可以了)
因为局部变量会被系统释放掉,那么它的引用自然也没了意义,会产能为垃圾值
2、函数的调用可以作为左值
例:因为返回的是一个引用类型,直接指向static a = 10; 可以只接改变它进行数据的操作,也可以创建另一个引用来引用它,就说明可以进行多重引用
int& f() {
static int a = 10;
return a;
}
int &b = f();把一个引用类型的返回值赋给另一个引用类型,和下面一样是多重引用
int &c = b;
f() = 50; 直接对引用类型的返回值进行赋值操作,从而改变内存中值的大小
常量引用:主要用来修饰形参防止误操作
const int & ref = 10 加入const之后变为只读状态,防止误操作
防止误操作如下:
void showValue(const int & val) {
val = 1000;//error,加了const之后便不能对val进行修改了,从而防止误操作
cout << val << endl;
}

函数:

c++中函数的形参可以给默认值
如果调用时没传参数,那么就使用默认值
注意事项:1、如果某个位置有了默认参数,那么从这个位置往右都要有默认参数
2、如果函数的申明里有了默认参数,那么函数的实现就不能有默认参数

函数占位参数:

1、只写数据类型,而没写形参	void func(int) {......}	
2、占位参数也可以有默认参数 void func(int = 10) {......}

函数重载:

可以让函数名相同,提高函数的复用性
满足条件:1、在同一个作用域下
2、函数名称相同
3、函数的参数类型不同,或个数不同,或顺序不同
注意事项:1、引用作为函数重载条件(以下两个函数是可以构成函数重载的)
void func(int &a) {} fun(a) 调用时传入一个变量
void func(const int &a) {} fun(10) 调用时传入一个常量,构成了引用常量 const int &a = 10 合法的代码,所以才能成功
2、函数重载碰到默认参数
void func(int a) {} 如果调用时写func(10)那么会出现二义性,两个函数都能访问,所以出错
void func(int a, int b = 10) {} 但是写func(10,20)就是没问题的,明确传入有两个形参的函数

封装:

class Yuan {

public: //权限
int r; //属性
public:
double c;
//行为
double f() {
return r;
}
};

访问: 1、Yuan y; 定义一个对象
2、y.r= 10; 访问属性
3、y.f() 访问函数

封装的访问权限:

public	公共的	成员类内可以访问,类外可以访问
protected 受保护的 成员类内可以访问,类外不可以访问,继承时可以访问
private 私有的 成员类内可以访问,类外不可以访问,继承时不可以访问

在c++中struct与class的区别

1、struct 	默认权限为公有
2、class 默认权限为私有
权限是类内成员的权限,而不是类的权限

构造函数(初始化):

创建对象时对对象的成员属性赋值
没有返回值,不写返回值 类名() {}

析构函数(清理):

在对象销毁前系统自动调用	
没有返回值,不写返回值 ~类名() {}
析构函数不可以有参数,因此不能发生函数重载
系统会自动调用,无需手动调用,而且只会调用一次

拷贝构造函数:

Person(const Person &p) {
age = p.age;
}

构造函数的调用:

1、括号法
Person p; 无参构造函数的调用(注意调用无参构造函数时不要写括号)
Person p1(10); 有参构造函数的调用
Person p2(p1); 拷贝构造函数的调用
2、显示法
Person p1 = Person(10);
注意:Person(p1) //error 不要利用拷贝构造函数,初始化匿名对象
3、隐式转换法
Person p3 = 10; //相当于Person p3 = Person(10)
Person p4 = p3; //隐式转换法的拷贝构造函数的调用

拷贝构造函数的使用时机:

1、使用一个已经构建完毕的对象来初始化一个新对象
Person p1(10); Person p2(p1) 拷贝p1里的内容来初始化自己
2、值传递的方式来给函数参数传值
void f(Person p) {}
Person p; f(p); 在把p传给函数f的形参时会默认调用拷贝构造函数来创建一个副本,原来的p便不会受影响
3、值方式返回局部对象
Person f() {
Person p;
return p; 返回局部对象会返回一个拷贝的副本,则会调用一个拷贝构造函数
}


构造函数调用规则:

c++会默认给一个类添加三个函数:默认构造函数(空实现),默认析构函数(空实现),默认拷贝构造函数(值拷贝)
如果自定义有参构造函数,则系统不会提供默认无参构造函数,但会提供默认拷贝函数
如果用户自定义拷贝构造函数,那么系统则不会提供其他构造函数

深拷贝与浅拷贝:

浅拷贝:简单的复制拷贝操作
编译器为我们做的是浅拷贝,简单的赋值操作 age = p.age
浅拷贝带来的问题:堆区的内存重复释放
深拷贝:在堆区重新申请空间,进行拷贝操作
例:解决重复释放的问题,用深拷贝来解决浅拷贝的问题
自己实现拷贝构造函数:m_Height = new int(*p.m_Height);

初始化列表:

	语法:	Person(int a, int b, int c):m_A(a),m_B(b),m_C(c) {
}
等价于(用来代替等号赋值)
Person(int a, int b, int c) {
m_A = a;
m_B = b;
m_C = c;
}

类对象作为类成员:

class A {}
class B {
A a;
}
会先构造类内的对象a,再构造自身B
先析构自身b,再析构类内对象a

静态成员:

静态成员变量:	1、所有对象共享一份数据
2、在编译阶段分配内存
3、类内声明,类外初始化
静态成员函数: 1、所有对象共享一个函数
2、静态成员函数只能访问静态成员变量,不能访问非静态成员变量

调用方法: 1、通过对象访问 p.func()
2、通过类名访问 Person::func()
类内声明类外初始化:
class Person {
public:
static int a; //类内声明
}
int Person::a = 0; //类外初始化

this指针:

this指针指向被调用的成员函数所属的对象
用途: 1、区分同名的形参与成员变量,this指针和java一个写法:this->name = name; 指针要用 -> 而不是用 .
2、在类的非静态成员中返回对象本身时,可以使用return *this
例: Person {
Person& func() {
this->age += age;
return *this; }
};
}
调用时可以Person p1;
p1.func().func().func(); 链式调用,因为返回值是当前的对象,调用函数结束后p1.func()还是一个对象,就可以再次调用函数

空指针调用成员函数:

可以调用没有访问任一属性的函数

const修饰成员函数:

1、const修饰成员函数称为常函数	void f() const {}
2、常函数内不可以修改成员属性
3、成员属性声明加了关键字mutable后,在常函数中就可以修改
4、声明对象前加const称为常对象 const Person p;
5、常对象只能调用常函数

友元:

关键字friend,让一个函数或类,能够访问另一个类内的私有成员
1、全局函数做友元
语法:将想要去访问的函数声明加上friend写在被访问的类内最前面
例:friend void goodFriend(Building *building); 写在class Building类下的第一行,将goodFriend函数作为了友元
2、类做友元
例:friend class goodGay; 写在被访问Building类的第一行,写一个想要访问的goodGay类的声明
3、成员函数做友元:
例:friend void goodGay::visit(); 要写成员函数对应的类名

运算符重载:

对已有的运算符进行重新定义,赋予其另一种功能
1、加号的运算符重载
成员函数: Person operator+ (Person &p) {
Person temp;
temp.a = this->a + p.a;
temp.b = this->b + p.b;
return temp;
}
调用:Person p3 = p1.operator+ (p2);
简化为: Person p3 = p1 + p2;
全局函数:Person operator+ (Person &p1, Person &p2) {
Person temp;
temp.a = p1.a + p2.a;
temp.b = p1.b + p2.b;
return temp;
}
调用:Person p3 = operator+ (p1, p2);
简化为: Person p3 = p1 + p2;
2、左移运算符的重载
成员函数: void operator << ( ostream &cout ) {
cout << a << "\t" << b << endl;
}
调用时系统会简化为p << cout,所以不推荐使用,只能利用全局函数实现
全局函数: ostream & operator << (ostream &cout, Person &p) {
cout << p.a << "\t" << p.b ;
return cout;
}
调用时 cout << p << endl 可以接着再往后追加,是因为返回值是一个ostream类型的引用,就是个链式编程的思想了



3、递增运算符的重载 	
前置++运算符的重载
MyInterger& operator++() {
num++;
return *this;
} 返回引用可以一只对同一个对象做操作
后置运算符的重载 后置++返回的是一个值,而不是引用
MyInterger operator++ (int) { 用占位参数(int)区分前置和后置
MyInterger temp = *this;
num ++;
return temp;
}
4、赋值运算符的重载
Person& operator= (Person &p) {
if(age != NULL) {
delete age;
age = NULL;
}
age = new int(p.age); //深拷贝
return *this;
}
5、关系运算符的重载
bool operator== (Person &p) {
if(this->name == p.name && this->age == p.age) {
return true;
}
return false;
}
6、函数调用运算符的重载,重载小括号,也被称为仿函数
void operator() (string test) {
cout << test <<;
}
调用: MyPrint myPrint;
myPrint("Hello World");
仿函数非常灵活,没有固定的写法
int operator() (int num1, int num2) {
return num1 + num2;
}

继承:

语法:class Son : public Father {}	子类:继承方式(public) 父类,来继承想要继承的类
子类也称为派生类,父类也称为基类
继承方式一共有三种:不管哪种父类的私有成员都不能被继承
1、公共继承
继承父类的public与protected成员,并且保持他们原有的权限
2、保护继承
继承父类非private的成员,并且全部更改为protected权限,保护权限在类外访问不了,可以访问的是:类自身,子类,友元
3、私有继承
继承父类非private的成员,并且全部更改为private权限
父类中所有的非静态成员都会被继承下去,只不过父类的私有成员被编译器隐藏了,是访问不到的

在vs工具中查看类的对象模型(VS 2017的开发人员工具命令提示符)

来到对应目录下输入: cl /d1 reportSingleClassLayout类名 "xxxxx.cpp"

继承中构造和析构的顺序:

父类的构造函数→子类的构造函数→子类的析构函数→父类的析构函数

继承中同名成员处理方式:

访问父类的同名成员加上父类的作用域就行了
例:s.Father::age; 访问函数同理
子类中有与父类同名的函数时,那么父类中所有的同名函数都会被隐藏(包括重载的函数),加上父类的作用域才可以访问

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

	静态成员与非静态成员出现同名:访问子类同名成员直接访问,访问父类同名成员需要加作用域
通过类名方式访问父类静态成员Son::Father::func()

多继承语法:

class 子类:继承方式 父类1,继承方式 父类2....... {  }
实际开发中不建议多继承,可能会有同名成员,访问时需要加作用域

菱形继承

概念:两个派生类继承自一个基类,而这两个派生类又被同一个类所继承
利用虚继承解决菱形继承二义性(继承了两个年龄)的问题:加上virtual关键字,Animal类叫虚基类
class Animal {punlic: int age};
class Sheep :virtual public Animal {};
class Tuo :virtual public Animal {};
class SheepTuo :public Sheep, public Tuo {};
加了virtual后,age只有一份了,并且可以直接st.age用子类的对象来赋值与访问

重写与重载的区别:

重写是函数名、形参列表、返回值类型都不变,改变方法体
重载是函数名相同,但形参列表不同

多态:

静态多态和动态多态的区别:
1、静态多态的函数地址早绑定 - 编译阶段确定函数地址
2、动态多态的函数地址晚绑定 - 运行阶段确定函数地址
在父类的函数前加上virtual,使其变为虚函数,继承的子类在重写此函数时就可以根据传入的对象来指向对应的子类
多态满足条件:有继承关系,子类重写父类中的虚函数
多态使用条件:父类指针或引用指向子类对象

纯虚函数和抽象类:

纯虚函数语法:virtual 返回值类型 函数名 (参数列表)= 0;
只要有一个纯虚函数,这个类就叫抽象类
抽象类特点: 1、无法实例化对象
2、子类必须重写抽象类中的纯虚函数,否则也是属于抽象类

虚析构和纯虚析构:

用于解决多态使用时,有属性开辟到了堆区,父类指针在释放时无法调用到子类的析构代码
将父类中的析构函数改为虚析构或纯虚析构
虚析构与纯虚析构共性: 1、都能解决父类指针释放子类对象的问题
2、都需要有具体的函数实现
虚析构与纯虚析构区别: 如果是纯虚析构,呢么该类属于抽象类,无法实例化对象
虚析构语法: virtual ~类名() {}
纯虚析构语法: virtual ~类名() = 0; 这是个声明
纯虚析构要在class外写出实现,并要写作用域,例:Animal::~Animal() { }

文件操作:对文件操作要加#include

文件类型:	1、文本文件 - 文件以文本的ASCII码存在计算机中
2、二进制文件 - 文件以二进制的形式存在计算机中,用户一般不能直接读懂它们
操作文件的三大类: 1、ofstream - 写操作
2、ifstream - 读操作
3、fstream - 读写操作
文本文件:写(读)文件步骤:
1、包含头文件 #include<fstream>
2、创建流对象 ofstream ofs;
3、打开文件 ofs.open("文件路径",打开方式); 读文件时多一步:ifs.is_open()判断文件是否打开成功
4、写数据(读数据) ofs << "写入的数据"
5、关闭文件 ofs.close();
文件打开方式:
ios::in 为读文件而打开文件
ios::out 为写文件而打开文件
ios::ate 初始位置:文件尾
ios::app 追加方式写文件
ios::trunc 如果文件存在,先删除,再创建
ios::binary 二进制方式
可以使用 | 操作符来配合使用

读数据的四种方法:	
第一种: char buf[1024] = { 0 };
while (ifs >> buf) {
cout << buf << endl;
}
第二种: char buf[1024] = { 0 };
while (ifs.getline(buf,sizeof(buf) ) ) {
cout << buf << endl;
}
第三种: string buf;
while (getline(ifs, buf)) {
cout << buf << endl;
}
第四种: char c;
while((c = ifs.get() ) != EOF) { //END OF FILE文件尾
cout << c;
}
二进制方式读文件:打开方式 ios::binary 一样五个步骤
写文件时可以写任意文件,例:写入了一个对象p;ofs.write((const char *)&p,sizeof(Person));
二进制方式读文件:
例:读入二进制文件到创建的对象p中: ifs.open("person.txt",ios::in | ios::binary);//打开,之后要判断是否打开成功,用ifs.is_open()
ifs.read((char *)&p,sizeof(Person));

模板:

泛型编程主要利用的技术是模板
1、模板只是一个框架,不能直接使用
2、模板的通用并不是万能的

函数模板语法: 1、template<typename T> typename也可以用class
2、函数声明或定义

例: template<typename T>
void mySwap(T &a, T &b) {
T temp = a;
a = b;
b = temp;
}
使用的两种方法: 1、编译器自动推导
mySwap(a, b);
2、显示指定类型
mySwap<int>(a, b);
注意事项: 自动类型推导,必须要推导出一致的数据类型
模板必须要确定出T的数据类型才能使用
如果模板函数里没用到T,那只能强行给T指定一个类型才能正常使用
函数模板用自动类型推导不会发生隐式类型转换
函数模板用显示指定类型回发生隐式类型转换

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

	1、如果普通函数与函数模板都可以实现,那么优先调用普通函数
2、可以通过空模板参数列表来强制调用函数模板 例:func<>(a, b); 加上一个空的<>,可以强制调用函数模板
3、函数模板也可以发生重载
4、如果函数模板可以产生更好的匹配,优先调用函数模板

给模板一个具体数据类型的实现代码,传入的数据类型符合优先调用
例:在模板下追加
template<> bool myCompare(Person &a, Person &b) {......}

类模板语法:

1、template<class T>
2、类

声明模板时可以写多个类型,例: template<class A, class B>
调用时分别指定数据类型 Person<string, int> p("plf", 18);

类模板与函数模板的区别:

1、类模板没有自动类型推导的使用方式
2、类模板在模板参数列表中可以有默认参数,函数模板不可以
例:template<class A, class B = int> 给一个默认数据类型
调用不写就是默认的:Person<string> p("plf", 18);

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

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

查看模板参数类型:

typeid(T1).name()	会返回一个string类型,输出显示就能看到

类模板对象做函数参数:

1、指定传入类型
void func(Person<string, int> &p) {......}
2、参数模板化
template<class T1, class T2>
void func(Person<T1, T2> &p) {......}
3、整个类模板化
template<class T>
void func(T &p) {......}

类模板与继承:

1、继承父类的类模板时,子类在声明时要指定出父类的T的类型	
class Son :public Father<int> {......};
2、如果不指定,编译器无法给子类分配内存
3、如果想灵活指出父类中的T的类型,子类也需要变成类模板
template<class T1, class T2>
class Son :public Father<T2> { public:T1 age}; T2是用来指定继承自父类的T的数据类型,T1用于自身的数据类型

类模板的成员函数类外实现:

构造函数:	template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age) {.....}
普通函数: template<class T1, class T2>
void Person<T1, T2>::func() {.....}

类模板的分文件编写:

1、直接包含cpp文件	分了cpp和h文件,直接#include "person.cpp"
2、将声明和实现写到同一个文件,并更改后缀名为hpp(hpp是约定俗成的名称)hpp文件放在头文件一栏中

类模板与友元:

1、全局函数类内实现
在class下第一行friend void func() {...} 虽然写在class内,但并没有在权限后面,所以这是个全局函数,加friend访问类内的私有成员
2、全局函数类外实现
类内写声明
例:friend void func<>(Person<T1, T2> p); 要加<>,不然会和类外的实现无法连接起来
类外在写实现时不需要加Person作用域,因为这是个全局函数,并且要写在类内声明的类的上面,因为声明中有Person类,所以要把Person类的声明再在全局函数的声明上面写上,并且要加上template<class T1, class T2>表示这是一个模板类
例: template<class T1, class T2>
friend void func<>(Person<T1, T2> p) { ... }

STL:

概念:标准模板库(Standard Template Library)
广义上分为:容器(container)算法(algorithm)迭代器(itetator)
容器和算法之间通过迭代器进行无缝连接
STL几乎所有的代码都采用了模板类或模板函数

vector容器:#include 包含头文件

		vector<int> v;	新建对象
v.push_back(10); 尾插数据
遍历数据的三种方式:
1、vector<int>::iterator itBegin = v.begin(); //指向第一个元素的位置
vector<int>::iterator itEnd = v.end(); //指向最后一个元素的后一个位置
while (itBegin != itEnd) {
cout << *itBegin << endl;
itBegin++;
}
2、for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << endl;
}
3、利用算法要#include<algorithm> //标准算法头文件
for_each(v.begin(), v.end(), myPrint) //参数:起始位置,结束位置,打印函数函数名
vector容器中存放自定义数据类型:
vector<Person*> v;
v.push_back(&p1);
for (vector<Person*>::iterator it = v.begin(); it != v.end(); it++) {
cout << (*it)->name << "\t" << (*it)->age << endl;
}

vector容器嵌套容器:

	vector< vector<int> > v;
for (vector< vector<int> >::iterator it = v.begin(); it != v.end(); it++) {
for(vector<int>::iterator vit = (*it).begin(); vit != (*it).end(); vit++) {
cout << *vit << endl;
}
}

string容器:

构造函数:1、string()		创建一个空的字符串:string str
2、string(const char * s) 传入一个s来初始化字符串
3、string(const string& str) 使用另一个string对象来初始化字符串(拷贝构造)
4、string(int n, char c) 初始化n个c的字符串
赋值操作:1、等号赋值 str = "hello"
2、成员函数 str.assign("hello")
str.assign("hello", 4) 把字符串前4个赋值过来
str.assign("hello")
str.assign(str1) 把str1赋值给str
str.assign(10, 'a') 10个a赋值给str
字符串的拼接:
1、运算符+=重载 str += "World"; str += 'a'; str += str1;
2、成员函数 str.append("World") 将字符串追加到字符串结尾,也可以追加字符与string对象
str.append(str1, pos, n)将字符串str1从pos开始的n个字符串追加到字符串结尾
string的查找与替换:
1、查找 str.find("ab") 返回第一次出现位置(int),可以写上从第一个位置开始查,不写默认从0开始
str.rfind("ab") 返回最后一次出现的位置(int),找不到返回-1
2、替换 str.replace(pos, n, str1) 替换从第pos开始的n个字符为str1
string字符串比较:比较是按照ASCII码进行比较的,返回值:==(0),>(1),<(-1)
str.compare(str1) str与str1进行比较,例:str > str1
string字符存取:
单个字符存取方式: 1、str[n] 通过[]来存取单个字符
2、str.at(n) 通过成员函数at
string插入与删除:
插入:insert str.insert(pos, "hello") 在pos位置插入字符串
str.insert(pos, n, 'c') 在pos位置插入n个字符c
删除:erase str.erase(pos, n) 删除从pos开始的n个字符
string子串:
str1 = str.substr(pos, n) 返回从pos位置开始n个字符

vector容器:

概念:和数组非常相似,也称为单端数组
与数组区别:数组是静态空间,而vector可以动态拓展
动态拓展:并不是在元空间之后续借空间,而是找块更大的内存空间,然后将元数据拷贝新空间,释放原空间
vector构造函数:
vector<T> v; 默认构造函数
vector<T>v2(v.begin(), v.end()) 将v[begin(), end())区间中的元素拷贝给本身
vector<T>v3(m, elem); 构造函数将n个elem拷贝给本身,例:10个100
vector<T>v4(v3) 拷贝构造函数
vector赋值操作:
1、=赋值 v2 = v1;
2、assign成员函数
v2.assign(v1.begin(), v1.end()) 取v1区间的值来赋值给v2
v2.assign(10, 100) 赋值10个100
vector容量和大小:
empty() 判断是否为空
capacity() 容器的容量
size() 返回元素的个数
resize(num) 重新指定容器的长度为num,若容器变长,则以elem填充(默认不写为0),若容器变短,删除末尾超出容器长度的元素
resize(num, elem) 可以指定默认填充的值
vector插入和删除:
push_back(val) 尾插val
pop_back() 尾删
insert(pos, val) 迭代器指向位置pos插入val
insert(pos, n, val) 迭代器指向位置pos插入n个val
erase(pos) 删除迭代器pos指向的元素
earse(start, end) 删除迭代器start到end之间的元素
clear() 清空容器中所有元素
vector数据存取:
v.at(index) 返回index所指的数据
v[index] 返回index所指的数据
v.front() 返回容器中第一个元素
v.back() 返回容器中最后一个元素
vector互换容器:
swap(vec) 将vec与本身的元素互换
巧用swap收缩内存
例:vector<int>(v).swap(v) vector<int>(v)调用拷贝构造函数拷贝v建造一个匿名对象,匿名对象来与原有的v进行交换,那么,被交换过来的内存在执行完这一句后,就会被系统释放,回收原来v中多余的内存,注意括号不能少
vector预留空间:
reserve(int len) 容器预留了内个元素长度,预留位置不初始化,元素不可访问

deque容器:迭代器支持随机访问

deque容器是双端数组,可以对头部进行插入删除操作
与vector的区别: vector对于头部的插入删除效率低,deque对头部的插入删除速度比vector快
vector访问元素时的速度会比deque快
构造函数:deque<T> d; 默认构造函数
deque<T>d2(d.begin(),d.end()) 将d[begin(), end())区间中的元素拷贝给本身
deque<T>d3(m, elem); 构造函数将n个elem拷贝给本身,例:10个100
deque<T>d4(d3) 拷贝构造函数
void func(const deque<int> &d)
当形参用const修饰了后,那么普通的迭代器就无法使用了,要用const_iterator
例:deque<int>::const_iterator it = d.begin()
deque容器赋值操作:
1、=赋值 d2 = d1;
2、assign成员函数
d2.assign(d1.begin(), d1.end()) 取d1区间的值来赋值给d2
d2.assign(10, 100) 赋值10个100
deque容器的大小操作: 没有容量(capacity)
empty() 判断是否为空
size() 返回元素的个数
resize(num) 重新指定容器的长度为num,若容器变长,则以elem填充(默认不写为0),若容器变短,删除末尾超出容器长度的元素
resize(num, elem) 可以指定默认填充的值
deque容器插入和删除:
push_front(val) 头插val
pop_front() 头删
push_back(val) 尾插val
pop_back() 尾删
insert(pos, val) 迭代器指向位置pos插入val
insert(pos, n, val) 迭代器指向位置pos插入n个val
erase(pos) 删除迭代器pos指向的元素
earse(start, end) 删除迭代器start到end之间的元素
clear() 清空容器中所有元素
deque数据存取:
d.at(index) 返回index所指的数据
d[index] 返回index所指的数据
d.front() 返回容器中第一个元素
d.back() 返回容器中最后一个元素
deque排序:
#include<algorithm> //标准算法头文件
利用一个sort算法
sort(d.begin(), d.end()) (默认升序)对迭代器start和end区间内的元素进行排序
对于支持随机访问的迭代器容器都可以用sort算法,vector也可以

随机数的获取:

包含头文件#include <ctime>
在main第一行加入一个随机数种子:srand((unsigned int)time(NULL));
调用rand(),例:想要0-99随机数rand()%100,40-60:rand()%21 + 40

stack容器:

	先进后出的数据结构,它只有一个出口
栈中只有顶端的元素才可以被使用,因此栈不允许有遍历行为
栈是自顶向下的,所以栈底在最上面,栈顶在最下面
top() 栈顶的元素
push() 向栈里添加数据,称为进栈
pop() 在栈里删除数据,称为出栈
构造函数:stack<T> stk 默认构造
stack<T> stk(stk1) 拷贝构造
赋值操作:=赋值: stack<T> stk = stk1
数据存取:push(elem) 向栈顶添加元素
pop() 从栈顶删除第一个元素
top() 返回栈顶元素
大小操作:empty() 判断是否为空
size() 返回栈的大小

queue容器:

queue容器是一种先进先出的数据结构,叫做队列,队列方向从对尾到对头
队尾(back)只进,对头(front)只出,push向队尾添加数据,叫入队,pop在对头删除数据,叫出队
队列中只有对头和队尾才能被外界使用,因此不允许有遍历行为
构造函数:queue<T> que 默认构造
queue<T> que(que1) 拷贝构造
赋值操作:=赋值:queue<T> que = que1
数据存取:push(elem) 向队尾添加元素,入队
pop() 从对头删除第一个元素,出队
front() 返回对头元素
back() 返回队尾元素
大小操作:empty() 判断是否为空
size() 返回队列的大小

list容器:

功能:将数据进行链式存储
链表的组成:链表由一系列结点组成
结点的组成:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域
STL中的链表是一个双向循环链表,指针域有两个指针,一个指针指向下一结点位置,另一指针指向上一结点位置,循环表示最后一个节点的指针域不是指向NULL了,而是指向第一个结点的位置
链表优点:采用动态内存分配,不会造成内存浪费和溢出
可以对任意位置进行快速插入或删除
插入和删除都不会导致原有迭代器的失效,这在vector中是不行的
链表缺点:容器遍历速度没有数组快,占用空间比数组大
链表中的迭代器:只支持前移和后移,属于双向迭代器,不支持随机访问
构造函数:list<T> L1; 默认构造函数
list<T>L2(L1.begin(),L1.end()) 将L1[begin(), end())区间中的元素拷贝给本身
list<T>L3(m, elem); 构造函数将n个elem拷贝给本身,例:10个100
list<T>L4(L3) 拷贝构造函数
list赋值与交换:
=赋值 L2 = L1;
assign成员函数赋值
L2.assign(L1.begin(), L1.end()) 取L1区间的值来赋值给L2
L2.assign(10, 100) 赋值10个100
L2.swap(L1) 将L1中的元素与L2自身的元素做交换
list大小操作:
empty() 判断是否为空
size() 返回元素的个数
resize(num) 重新指定容器的长度为num,若容器变长,则以elem填充(默认不写为0),若容器变短,删除末尾超出容器长度的元素
resize(num, elem) 可以指定默认填充的值
list插入和删除:
push_front(val) 头插val
pop_front() 头删
push_back(val) 尾插val
pop_back() 尾删
insert(pos, val) 迭代器指向位置pos插入val
insert(pos, n, val) 迭代器指向位置pos插入n个val
insert(pos,beg,end) 迭代器指向位置pos插入[beg, end)区间的数据
erase(pos) 删除迭代器pos指向的元素
earse(start, end) 删除迭代器start到end之间的元素
clear() 清空容器中所有元素
remove(elem) 删除容器中所有与elem匹配的元素,例:remove(10)
list数据存取:不支持[]与at()访问,只能it++,而不能it = it +1
front() 返回第一个元素
back() 返回最后一个元素
list反转与排序:所有不支持随机访问的迭代器,不可以用标准算法,内部会提供一些成员函数,所以sort是个成员函数,而不是算法
L1.reverse() 反转链表
L1.sort() 排序链表(默认升序,从小到大)
降序: bool my Compare(int v1, int v2) {
return v1 > v2;
}
L1.sort(myCompare)
自己提供一个函数v1>v2返回true,将函数名传入sort
例:高级排序写法(按照年龄升序排序,年龄相同按照身高降序排序)
bool comparePerson(Person &p1, Person &p2) {
if (p2.age == p2.age) {
p1.height > p2.height;
}
return p1.age < p2.age;
}
L1.sort(comparePerson);

set/multiset容器:都是包含#include

set基本概念:所有元素在插入时自动被排序(升序)
本质:set/multiset属于关联式容器,底层结构用二叉树实现
set与multiset区别:set不允许有重复的元素,multiset允许有重复的元素
set构造和赋值:
set<T> st 默认构造
set<T> st(st1) 拷贝构造
=赋值 st2 = st1;
set大小和交换:
empty() 判断是否为空
size() 返回元素的个数
st2.swap(st1) 交换st1与st2
set插入和删除:
insert(val) 在容器中插入val
erase(pos) 删除迭代器pos指向的元素,返回下一个元素的迭代器
earse(start, end) 删除迭代器区间start到end之间的元素,返回下一个元素的迭代器
erase(val) 删除容器中值为val的元素
clear() 清空容器中所有元素
set查找和统计:
find(key) 查找key是否存在,若存在返回该键的元素的迭代器,不存在返回set.end()
count(key) 统计key的元素个数,对于set而言,要么0,要么1,因为不可以重复,multiset可以
set和multiset区别:
1、set不允许有重复的元素,multiset允许有重复的元素
2、set插入数据的同时会返回插入结果,表示是否插入成功
pair<set<int>::iterator, bool> ret = set.insert(10) 用pair对组接收 ret.second可以访问接收到的bool数据
3、multiset不会检测数据,因此可以重复插入
set容器排序:
1、set存放内置数据类型
指定排序为从大到小,例:
class MyCompare {
public:
bool operator() (int v1, int v2) {
return v1 > v2;
}
};
set<int,MyCompare>s; 插入数据之前就定好排序规则,模板列表中要放的是一个数据类型,所以要放类名,放函数名是不行的
2、set存放自定义数据类型
class MyCompare {
public:
bool operator() (const Person& p1, const Person& p2) {
return p1.age > p2.age;
}
};
set<Person,MyCompare>s; 存放自定义数据类型都要指定排序规则

pair对组

创建方式:1、pair<type, type> p (value1, value2)	创建并赋初值
2、pair<type, type> p = make_pair(value1, value2)
数据获取:p.first和p.second来获取第一个和第二个数据

map/multimap容器:

map中所有元素都是pair
pair第一个元素是键值(key),起到索引作用,第二个元素为实值(value)
所有的元素都会根据键值自动排序
属于关联式容器,底层结构是用二叉树实现
优点:可以根据key值快速找到value值
map与multimap区别:map不允许有重复的key值,multimap允许(key值有限制,但是value值都可以有重复的)
map构造和赋值:
map<T1, T2> mp 默认构造
map<T1, T2> mp(mp2) 拷贝构造
map<T1, T2> mp = mp2 等号赋值
map大小和交换:
empty() 判断是否为空
size() 返回元素的个数
mp2.swap(mp1) 交换mp1与mp2
map插入和删除:
erase(pos) 删除迭代器pos指向的元素,返回下一个元素的迭代器
earse(start, end) 删除迭代器区间start到end之间的元素,返回下一个元素的迭代器
erase(key) 删除容器中值为key的元素,删除一对元素
clear() 清空容器中所有元素
m.insert(pair<int, int>(1,10)) 在容器中插入pair对组,以下还有三种方式
m.insert(make_pair(2, 20)) 不用写模板参数
m.insert(map<int, int>::value_type(3, 30))
m[4] = 40;
map查找和统计:
find(key) 查找key是否存在,若存在返回该键的元素的迭代器,不存在返回map.end()
count(key) 统计key的元素个数,对于map而言,要么0,要么1,因为不可以重复,multimap可以
map容器排序:默认从小到大排序,利用仿函数改为了从大到小排序
class MyCompare {
public:
bool operator() (int v1, int v2) {
return v1 > v2;
}
};
map<int,int,MyCompare>mp; 改为了从大到小排序

函数对象

重载了函数调用操作符,也叫仿函数
函数对象(仿函数),本质上是一个类,不是一个函数
class MyAdd {
public:
int operator() (int v1, int v2) {
return v1 + v2;
}
};
MyAdd myAdd;
myAdd(10, 10);

谓词:

1、返回bool类型的仿函数称为谓词
2、如果operator()接收一个参数,那么叫做一元谓词
3、如果operator()接收两个参数,那么叫做二元谓词
class GreateFive {
public:
bool opeerator() (int val) {
return val > 5;
}
};

内建函数对象:#include

1、算术仿函数
T plus <T> 加法仿函数
T minux <T> 减法仿函数
T multiplies <T> 乘法仿函数
T divides <T> 除法仿函数
T modulus <T> 取模仿函数
T negate <T> 取反仿函数
使用示例:1、包含头文件
2、新建对象 plus<int> n;
3、调用仿函数 n(10, 20)
2、关系仿函数
bool equal_to<T> 等于
bool no_equal_to<T> 不等于
bool greater<T> 大于
bool greater_equal<T> 大于等于
bool less<T> 小于
bool less_equal<T> 小于等于
3、逻辑仿函数
bool logical_and <T> 逻辑与
bool logical_or <T> 逻辑或
bool logical_not <T> 逻辑非

STL常用算法:

算法主要由头文件algorithm,functional,numeric
algorithm:是所有STL头文件最大的一个,范围涉及到比较、交换、查找、遍历操作、赋值、修改等等
numeric:体积很小,只包括了几个在序列上进行简单数学运算的模板函数
functional:定义了一些模板类,用以声明函数对象
常用遍历算法:
1、for_each 遍历容器
for_each(v.begin(), v.end(), printValue) 第三个参数可以是普通函数名,也可以是个仿函数
for_each(v.begin(), v.end(), printValue()) 仿函数写法,传入printValue类的匿名对象
2、transform 搬运容器到另一个容器中
v2.resize(v1.size()) 目标容器需要提前开辟空间
transform(v1.begin(), v1.end(), v2.begin(), TransForm()) 原容器开始迭代器,原容器结束迭代器,目标容器开始迭代器,普通函数或仿函数
常用查找算法:
1、find 查找元素
find(v.begin(), v.end(), value) 找到返回摘到位置的迭代器,没找到返回结束位置迭代器v.end()
查找自定义数据类型时,要重载==号,不然无法进行当前遍历对象与目标之间的判断操作
bool operator== (const Person &p) {
if(this->name == p.name && this->age == p.age)
return true;
return false;
}
2、find_if 按条件查找元素,找到返回摘到位置的迭代器,没找到返回结束位置迭代器v.end()
find_if(v.begin(), v.end(), FindTest()) 起始位置迭代器,结束位置迭代器,谓词(返回bool类型的仿函数)或_Pred函数(普通函数返回bool)
3、adjacent_find 查找相邻重复元素
adjacent_find(v.begin(), v.end()) 找到返回相邻元素第一个位置的迭代器,没找到返回结束位置迭代器
4、binary_search 二分查找法,在无序序列中不可用
binary_search(v.begin(), v.end(), value) 查到返回true,否则返回false
5、count 统计元素个数
count(v.begin(), v.end(), value) 返回int数据,统计出现元素个数
统计自定义数据类型要重载==
6、count_if 按条件统计元素个数
count(v.begin(), v.end(), GreaterFive()) 传入谓词(返回bool类型仿函数)
常用排序算法:
1、sort 对容器内元素进行排序
sort(v.begin(), v.end()) 第三个参数不填默认升序,填的话填谓词,自己写排序规则
改为降序可以直接用greater<int>()的内置函数对象,要#include <functional>
2、random_shuffle 洗牌,对指定范围内元素随机进行次序调整
random_shuffle(v.begin(), v.end()) 给区间就可以
3、merge 将两个容器的元素合并,并存储到另一容器中,两个容器必须是有序的,并且顺序一致
v3.resize(v1.size() + v2.size()) 目标容器要提前分配空间
merge(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin())
4、reverse 反转指定范围的元素
reverse(v.begin(), v.end()) 将区间内的元素反转
常用拷贝和替换算法:
1、copy 指定范围内元素拷贝到另一容器中
copy(v1.begin(), v1.end(), v2.begin()) 要提前为v2分配内存
2、replace 指定范围内旧元素替换为新元素
replace(v.begin(), v.end(), oldVal, newVal)
3、replace_if 指定范围内满足条件的旧元素替换为新元素
replace_if(v.begin(), v.end(), GreaterFive(), newVal) 填入谓词(返回bool的仿函数)与新元素
4、swap 互换两个容器的元素
swap(v1, v2) 放两个容器,要是同种类型的容器(比如说都是vector)
常用算术生成算法: #include <numeric>
1、accumulate 计算容器元素累计总和
accumulate(v.begin(), v.end(), 0) 返回一个int值,第三个参数是起始的累加值(填100,则从100开始加)
2、fill 向容器中添加元素
fill(v.begin(), v.end(), value) 开始费带起,结束迭代器,添加的值
常用集合算法:
1、set_intersection 求两个容器的交集,两个容器必须是个有序序列,获取到交集后会返回交集的最后一个位置的迭代器
v3.resize(min(v1.size(), v2.size())) 分配空间时先两个集合较小的一个size就行,因为考虑到有包含关系的特殊交集存在
set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin())
2、set_union 求两个容器的并集,两个容器必须是个有序序列,获取到并集后会返回并集的最后一个位置的迭代器
v3.resize(v1.size() + v2.size()) 分配空间时两个size相加,因为考虑到有可能元素完全不一样
set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin())
3、set_difference 求两个容器的差集,v1与v2的差集(v1中不是v1与v2交集的部分叫差集)v2与v1的差集则是(v2中不是v1与v2交集的部分)
v3.resize(max(v1.size(), v2.size())) 分配目标容器为两个容器最大的一个的size
set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin()) 两个容器必须是个有序序列,获取到差集后会返回差集的最后一个位置的迭代器