一。
1.一个字节8个2进制,一个字母一个字节,汉语两个字节。
Ascii编码 英文数字。
Unicode编码所有语言,Ascii编码 是它的子集。
Utf-8 是Unicode的一种实现方式
Urlencode 是 前端和后端交互的编码
int 32位 short 16位
单引号和双引号的区别:
单引号表示是字符变量值,字母的本质也是数字。
双引号表示字符串变量值,给字符串赋值时系统会自动给字符串变量后面加上一个\0来表示字符串结尾
输出:%c 字符串 %f浮点数 %lf浮点数double %d 整数 \n 换行 printf()
%x, %X 无符号以十六进制表示的整数
cout << endl 换行
输出小数点多少位。如果超了会四舍五入
输入: scanf().字符输入空格也会被录入 cin.get 可以读入空格int型 EOF是否结束 cin>> 空格不会被录入
&a 使用值指针。 使用相同类型的io函数 freopen重定向文件输入
变量:类型决定占用多少字节 变量名相当于房间号 类型相当于户型。
变量是大小写相关的
sizeof() 获取变量字节数
先定义 后使用 给一个初始值 没初始的变量值不固定
类型名 变量名
一个变量不可以被定义两次
不同数据类型可以相互赋值 小数赋值整数 小数部分会被省略
字符赋值给整数就是ASCII码 、 整数给字符赋值就是最后一个字节ASCII码对应的字符 (会出错)
字符串不能给字符和整数赋值
字符串可以包含转义字符
0x 开头表示16进制
常量:
大写一般 、#define 不可被更改
运算符:
两个类型运算,取精度更高的为准
运算结果超过结果类型,会造成溢出,溢出部分直接丢弃。 解决方案:尽量使用高精度的数据类型进行运算
两个整数做除法余数会被丢弃
除法运算和模运算都不能为0
++a 先+1在返回值 a++ 先返回值在+1
基础语法
else if c++分开写。只有一个语句不用{}
switch
case
break
default
for(int i=0;i<26;++i){
printf(’%c’,‘a’+i);
}
for(int i=15,j=0;i>j;i-=2,j+=3){
printf(’%c’,‘a’+i);
}
for循环里的表达式可以不写 break循环中跳出
while(true){ //无法预估循环次数,达到某个条件跳出循环
}
数据类型:
数组:标示相同类型元素的集合,集合的名字就是数组
类型名 数组名[元素个数常量]
int a[100] 数组下标从0开始
数组初始化 值之间逗号分隔 ,缺省部分自动为0
int a[5] = {1,2,3};
定义数组时可以不给定元素个数,初始化的值为个数
数组越界:容易犯的错,不会编译报错。内存出core (内存越界) 十分难以查找,编写程序时候要注意
可以尽量申请数组的时候开大一点,防止数组越界
多维数组 a[0][0] 位置最小的
可以直接访问类似a[1]
初始化 int a[2][3]={{1,2,3},{2,3}};
进行初始化时可以不写行数
a[0][3]={{1,2,3},{2,3}};
函数:
可以返回void类型
int Max(int x ,int y[]){ //定义为形参
return x;
}
Max(1,2); //调用为实参
函数的形参是实参的拷贝,形参改变不会影响实参
数组(实质是指针,数组的首地址)、引用和对象时会影响实参
局部变量可以和外部变量同名
二维数组作为函数的形参时,必须写明多少列,可以不用写明多少行
int Max(int y[][2]){
return y;
}
一般函数定义都在调用之前 (如果存在相互调用,可以先在之前声明,之后在实现)
函数声明 只有函数的入参和返回值定义,没有具有实现// 入参的名称可以省略,类型不可省略
int Max(int x ,int y);
main 函数为主入口
C++ 区分函数大小写
库函数:
#include //包含io操作的库函数
#include //包含字符串库函数的声明
#include //包含数学库函数头文件,很多函数的声明。把头文件里的内容都负责复制粘贴到文件
sqrt(a); //标准库函数可以直接调用
位运算:速度很快
字符串: " 双引号转译 ,都是用\0标示结尾
- ""形式
- 存放字符数组以 ‘\0’ 字符结尾。 Char a[100] // a[2]=0; 就是写入了一个’\0’。数组就是地址。scanf("%s",line) cin 只读到输入的空格为止。 容易数组越界
cin.getline(char buf[],int bufSize) //解决空格输入和越界问题
gets() //可以解决空格,不能解决越界问题
3.string对象,标准模版库的一个类
strlen() //求字符长度的,不包含\0 \0 就是整数0 可以直接用于做false判断
strcmp() //字符串比较函数 s1大于s2 返回正数 0 负数
strcpy() //字符串拷贝函数 入参是字符数组,也可传字符串常量 常量不可以被改变 ,如果传入两个数组长度不同,可能导致数组越界,字符串直接赋值,指针不变。两个变量指向同一个地址。
strcat() //字符串拼接。如果被拼接的数组内容不够大,也可能导致数组越界
strtok(str,",-"); //字符串根据",-“分割,返回第一个分割指针,继续调用第一个参数是NULL strtok(NULL,”,-");
以数组作为参数时,形参不是实参的拷贝,类似是指针。
int pos=s.find("+",0); //发现字符串s中是否有"+"号
real=s.substr(0,pos); //分离字符串
循环内部不要放入需要重复计算语句
指针:
int * p //类型名 * 变量名
int * p =(int *) 4000; //赋值给p地址4000 很少用 强制类型转换
*p = 5000; //*p代码地址4000所指向的内容
char ch1=‘A’;
char * pc= &ch1; //取地址运算符。取变量ch1的地址 通常的用法,取变量的地址,可以读写该变量
不同类型的指针不经过强制类型转换不可以直接赋值,编译会出错 谨慎使用,各类型字节数不同,容易越界
指针的运算:
1.相同类型指针可以比较大小,相同时为真 地址1大于地址2
2.相对类型指针可相减 p1-p2 =(地址p1-地址p2)/sizeof(T) p1与p2之间可以存放多少T类型的变量
3.指针变量可以加减一个整数,其结果还是一个指针 p+n*sizeof(T)
4.指针变量可以自增自减 p++ p+sizeof(T)
5.指针可以通过下标访问p[n] 等于 (p+nsizeof(T))
空指针 指向地址0 不可访问 int * pn=NULL 或 int * pn=0; if§或if(p!=NULL)
指针与数组:数组的名字就是一个指针常量,指向数组的起始地址。
int a[10] a的类型是 int * a 可以给int * 类型的指针赋值 a是编译是确定的常量 不可以被赋值 数组名是指针常量
函数入参 :int * p 和int p[] 等价都是指针或数组
字符串常量 是 char *
字符数组名也是 char *
很多函数的入参是指针,要注意
void指针:可以用任何类型的指针对void指针进行赋值或初始化
sizeof(void)没有定义,p 、++p 等均无定义
double d = 1.11;
void * p = &d;
内存操作函数:
赋值 memset(a,0,sizeof(a)) 对内存地址a前n个字节都赋值为c进行赋值 头文件cstring
拷贝 memcpy(a,b,10sizeof(int)) 将地址B的n个字节赋值到A
函数指针:
函数的入口地址
返回值类型 * 变量名(参数1类型,参数2类型);
int (* pf)(int, char);
int PrintMin(int a, char b){
}
pf = PrintMin;
pf(x,y);
排序:
qsort //快速排序 入参: 数组地址 个数 数组内容格式字节数 比较函数指针(入参是两个数字内容类型的指针)
struct //结构. 自己定义的数据类型 不同变量可以相互赋值。可以嵌套 结构内部成员是其他结构。 也可以是指向本结构的指针。 也可以被定义成数组
struct Student{
int a;
}
Student s1={123}; //实例 初始化
s1.a; //使用结构中的变量,用.访问
结构变量指针。//可以用两种写法 (*pStudent).a; pStudent->a;
Student * pStudent;
pStudent = &s1;
全局变量和局部变量
函数内部叫局部变量,函数外部叫全局变量
全局变量都是静态变量,局部变量前面加 static 就变成了静态变量
静态变量在程序运行期间,存放地址都是固定不变的,初始化都是0,只初始化一次,第一次调用的时候被初始化
非静态变量,每次函数运行时,地址可能变化,初始化是随机的。
作用域都在最内层的{}里
生存期:占有内存,全局变量的生存期,从程序被装入内存开始,到程序结束。
引用。//某个变量的引用,等价于这个变量,相当于这个变量的一个别名。 初始化成引用哪个变量,不会在引用其他变量
只能引用变量,不能引用常量和表达式
int n=4;
int & r = n; //r引用了n,r的类型是int &
int & r1 = r; //引用可以引用类型
int & SetValue(){return n;} //可以引用函数返回值。 相对引用了n
SetValue() = 40; //此时n为40
const int & r = n; //可以用常饮用,r的类型为const int & 不能修改内容 如r=20; 会编译错误。n=20; 本身改变没问题 相当只能读不能写
const T & 类型不能初始化 T &类型
T & 类型可以初始化 const T &类型
const
const int MAX = 10; //常量一般为大写
const int * p = &n; //常量指针 不可以通过常量指针修改其内容。指向的内容可以通过其他方式修改 常量指针的指向也可以变化。不能把常量指针赋值给非常量指针,反过来可以 强制类型转换也可以
作为函数参数时,一般认为在这个函数内部,不应该被修改
new //动态内存分配,对应静态内存分配,在编译好时候决定。C++一般都是常驻进程的
p=new int; //p为 int *的指针
pn=new int[100]; //pn int *的指针. 注意不要数组越界
delete p ;//动态分配的内存空间一定要释放掉 ,一个空间不可以多次delete,会异常
delete []pn ; //释放动态分配的数组
内联函数 //调用内联函数时不会 执行调用函数语句,直接把内联函数对应的代码复制到调用出,节省函数调用的时间开销
inline int Max(int x ,int y[]){ //定义为形参
return x;
}
函数重载。//一个或多个函数 名字相同,然而参数类型或个数不同。 使函数命名变得容易 如果参数相同会报重复定义的错误
调用的时候不应该具有二义性
Max(3,2.4); //这种容易出错,应改为Max(3.0,2.4);
函数缺省 //在最右侧赋默认值。调用时候可以缺省。 提高程序的可扩充性。
面向对象:
类与类的关系
客观事物的属性:变量
能对事物的行为:函数
Class CR{ //类的名字 就是自定义类型的名字
puvlic :
int w , h;
void Init(){} //一般作为初始化
void InitA();
};
CR r; //实例化一个对象
函数或变量的用法
- R.Init(); //对象.函数或变量
- p->Init(); //指针->函数或变量
- R. Init(); //引用.函数或变量。作为函数的参数 更改类的变量
对象之间可以用=赋值,不能比较,除非运算符重载,类的函数内存只有一份,对象在内存中只存储变量,内存字节数,为变量之和。
类的函数定义和声明可以分开写,先在类里定义,声明写在类外面;函数前面加类的名字
void CR:: InitA(){};
当保护类型为缺省时默认为私有成员 private
类的内部函数 可以访问当前类的所有成员和相同类其他对象的所有成员
成员函数也可以重载和缺省。 避免重载之和参数缺省的二义性
构造函数 和类名一致 不能有返回值 (void也不行)
在已分配的内存空间上,对对象进行初始化,默认无参数,什么都不操作
一但定义,编译器就不会生成默认的构造函数了
也可以重载
对象数组 调用构造函数 传入参数,无参数默认调用无参构造函数。 默认参数传递 可能数组之前传参不一致,有的有,有的没有
CR arr[2]={4,5};
D对象指针也可以初始化对象,调用构造函数
复制构造函数:
X::X(const X &); //该类的对象,作为参数,实现对象的复杂,编译器会自动生成
一个对象初始化同类的另一个对象、作为函数参数或返回值,进行形参的拷贝、
只有初始化会调用,赋值语句不会调用。
类型转换构造函数:
实现类型的自动转换、
CR r=12; //调用构造函数
r =13; //因为存在类型转换构造函数,所以会将13转换为一个临时CR类的tmp对象,然后复制给r
析构函数:
在类对象消亡时候会被调用,跟类同名前面带~ 只能有一个
~CR 释放内存分配等。默认的析构函数什么都不做
this指针
非静态成员函数中可以直接使用this凯代表函数作用的对象的指针。静态成员函数不可以调用this指针,静态函数不作用具体某个对象
this->ddd = ‘ss’;
return *this;
A * p = NULL; //不存在调用成员变量时候,空指针可以调用成员函数,因为类的函数在内存中一份,有调用成员变量的空指针不能用。
P->Hello();
静态成员和静态函数:
普通成员变量每个对象各自一份,而静态成员变量一共就一份,为所有对象共享。
sizeof不会计算静态成员变量
普通成员函数必须具体作用于某一个对象,而静态成员函数并不具体作用于某个对象。
静态成员不需要对象就能访问。
CR::Hello();
CR r; r.Hello();
CR * p = &r ; p->Hello();
静态成员变量本质上是全局变量,哪怕一个对象都不存在,类的静态成员变量也存在。
静态成员函数本质上就是全局函数
必须在定义类的文件中对静态成员变量进行一次声明或初始化。 否则会报错
函数初始化列表
CR(int r, int w):rad®,width(2){} //初始化赋值语句的简写,一般用于构造函数
封闭类:
成员对象包括其他类的对象。
调用构造函数的时候要调用类对象的构造函数。
如果成员对象写了构造函数,需要有参数,在构造之前一定要调用,如果无参构造函数初始化会出错。
任何生产封闭类的对象,都要让编译器明白,对象中的成员对象,是如何初始化。
执行顺序:先执行所有对象成员的构造函数,然后在执行封闭类的构造函数
先执行封闭类的析构函数,然后在执行所有对象成员的析构函数
栈的形式:先入后出。
复制构造函数也会调用对象成员的复制构造函数。
常量对象const:
可以在对象前面加const使生成的对象被初始化之后值不会被更改。也不可以调用非常量成员函数
const Demo Obj;
常量成员函数:常量成员函数执行期间不应修改其所作用的对象,不能修改成员变量的值(静态成员变量除外)也不能调用同类的非常量成员函数(静态成员函数除外)
Class CR{ //类的名字 就是自定义类型的名字
puvlic :
int w , h;
void Init(){} //一般作为初始化
void InitA()const; //常量成员函数
};
两个成员函数的名字相同,一个是带const的常量成员函数一个不带算是重载。
引用前面加const关键字,表示不能通过这个引用改变变量的值,一般用了作为函数的参数。
友元:friend
友元函数:友元函数不是对象的类成员函数,但可以访问类的私有变量
友元类:如果A是B的友元类,那么A的成员函数可以访问B的私有成员。
class B{
friend class A; //声明友元类
friend void A::func(); //声明友元函数
}
友元类的关系不能传递和继承
运算符重载:
运算符只能用于基本数据类型的运算,为了支持自定义类型的运算,运算符重载的实质就是函数重载。
A operator+(A a1,A a2){ //可以重载为全局函数 + 可以通过友元使之可以访问私有变量 已支持 5+A
return A(a1.x+a2.x);
}
C=A+B; //等价 C= operator+(A+B);
A A::operator-(A a2){ //也可以重载为类的成员函数 - 重载为成员函数时,参数个数为运算符目数减一
return A(x-a2.x);
}
A-B // 等价 A.operator+(B); 返回值仍应用于A
赋值运算=重载:只能是成员函数 只能是赋值,不能是初始化。必须为类的成员函数
A & A::operator=(const char * s){
strcpy(str,s);
return *this ;
}
浅拷贝和深拷贝:
浅拷贝:如果让两个类用原生的=进行赋值,就是浅拷贝,指向同一个内存地址。str指向同一个地址 直接更改容易产生内存垃圾,没有delete掉 容易两次析构出错。变量消亡.
复制构造函数也会存在这个问题,也要重载这个,要不也会指向同一片内存空间
A1=A2;
深拷贝:对相同类的对象实现赋值运算=的重载
A & A::operator=(A a){ //返回原因对象的引用可以保持连等的特性 (a=b)=c;
if(this= &a){
return *this ; //兼容s=s 这种形式 防止 delete[] str;
}
strcpy(str,a.s);
return *this ;
}
可变长的数组类:
数组下标的引用[] 返回值为数组内容类型的引用 支持a[i]=4; intx=a[i]; 必须为类的成员函数
#include 标准模版库可变长数组
vector array(len);//声明变长数组
流插入和流提取的重载运算符
ostream &operator<<(ostream & o,const A & a){ //只能重载为全局的函数 输出
o<<a.x;
return o;
}
istream &operator<<(istream & is,const A & a){ //输入
is>>a.x;
return is;
}
类型转换运算符重载:
基础数据类型的名字本身也是类型转换运算符
class B{
operator double(){return x}; //类型转换运算符double重载. 不用写返回值,本身类型转换运算代表了返回值类型
}
B b; (double)b;
自增自减运算符重载:
T & operator++(){++x; return * this;} //前置运算符++作为一元运算符重载 前置运算符返回的是引用 后置则是临时对象
T operator++(int ){T2 tmp(*this);x++; return tmp;} //前置运算符++作为二元运算符重载 ,参数没用处
//重载为全局函数
T operator++(T & t,int){T tmp(t);t.x++; return tmp;}
前置++直接返回引用 后置要生成临时对象所以更慢 从速度上看写++i 更快
运算符重载不改变原有运算符的优先级
继承和派生:
派生类包括基类全部的成员变量包括私有和保护
派生类的成员函数不可以访问基类的private成员
派生类的成员与基类相同,会覆盖 ,缺省时访问子类的成员,调用父类的同名成员函数,需要在前面写上基类的名字
正常编程过程中,不要在基类和子类写同名的成员变量,同名函数可以写。
class A :public B{ //A继承B.
B::func(); //调用父类的同名成员函数,需要在前面写上基类的名字
};
继承关系 :是B的同时一定是A
复合关系 : B包含A的对象,A一定是B的组成部分
知道关系:B包含A的指针数组
保护型成员变量:
基类的成员,基类的友元,派生类的成员函数可以访问当前对象的基类的保护成员,派生类的子类不可以访问。成员对象也不可以访问。
派生类的构造函数:在初始化列表里先调用父类的构造函数 ,默认调用基类的无参构造函数
CR(int r, int w):father(r,w)}
先执行派生类的析构函数在执行基类的析构函数
如果有基类还有成员类 :先构造基类,在构造成员类 ,消亡时正好相反执行析构函数顺序
public继承的赋值兼容规则:
派生类对象可以赋值给基类对象,基类对象不可以赋值给派生类对象;
A=B;
派生类对象可以初始化基类的引用:
base &br=d;
派生类对象的地址可以赋值给基类指针:
base * pd=&d;
如果派生方式是private或protected 则上述三条不可行
直接基类和间接基类:只写直接基类就可
虚函数和多态
虚函数:构造函数和静态成员函数不能是虚函数
virtual int get(); //在基类里定义写virtual,函数声明时不用写
int base::get(){}
当基类指针和派生类中同名虚函数时
基类的指针调用虚函数时,调用基类的虚函数
派生类的指针调用虚函数时,调用派生类的虚函数
基类的指针指向派生类对象和基类的引用赋值为派生类对象,调用派生类的虚函数
多态对程序的扩展性很好,用基类的指针做参数,新增派生类时,不需要改其他派生类的交互
用基类的指针数组存放指向各种派生类对象的指针,然后遍历该数组,就能对各派生对象进行操作。
在非构造函数、非析构函数的成员函数中调用虚函数,就是多态
在构造和析构函数里调用不是多态,只是调用自己的函数
多态的实现:包含虚函数的实例都有虚函数表。
虚析构函数:把基类的虚构函数定义为virtual,可以解决通过基类指针指向派生类对象时,删除派生类对象只调用基类的析构函数的问题。这样删除派生类对象,首先调用派生类的析构函数
一般来说、一个类如果定义类虚函数或一个类打算作为基类,则应该将析构函数定义为虚函数。
不可以以虚函数作为构造函数。
纯虚函数和抽象类
没有函数体的为纯虚函数,基类只声明没定义
virtual int get()=0;
包含纯虚函数的类称为抽象类
只能作为基类使用,不能创建独立的对象,抽象类的指针可以指向抽象类派生的对象
抽象类的成员函数内可以调用纯虚函数,构造函数和析构函数不可以调用
派生类必须实现所有纯虚函数,才能成为非抽象类
输入输出和模版:
重定向将标准输出、输入,重定向到test.txt 文件
freopen(“test.txt”,“w”,“stdout”); //cout << x;
freopen(“t.txt”,“r”,“stdin”); //cin>>n>>x; t.txt中 1 2
while(cin>>x){ //判断输入是否结束 最后一行为结束 键盘ctrl+z 代表结束
}
getline(); //按行读取,自动增加\0
if(!cin.getline(char * buf,int bufSize,char delim)){ 每次读取一行或按分割符获取bufSize以内字符
}
bool eof(); //判断输入流是否结束
int peek(); //返回下一个字符,但不从流中去掉
istream & putback(char c); //将字符ch放回输入流
istream & ignore(int nCount=1,int delim=EOF ); //从流中删掉最多nCount个字符,遇到EOF时结束
cerr << “error”<<endl; //标准错误输出,输出到屏幕 endl换行
流操作算子:控制程序输出格式
#include
cout<<hex<<n<<"\n"; //16进制输出
cout.precision(5);//控制浮点数精度的
setiosflags(ios::fixed); //定点方式输出小数, 非定点为有效数字
resetiosflags(ios::fixed); //取消定点方式输出小数
cin.width(5); //设置域宽的操作算子,不够位数用空格补位,超过就对应截取输出,是一次性的,每次都要设置 , 包含\0
\t 制表符
文件的读写
#include //文件读写头文件
ofstream outFile(“tmp\clients.dat”,ios::out|ios::binary); //生成输出文件当前文件夹clients.dat,删除原有内容,以二进制格式打开
ios::app //在尾部添加,原有内容不删除
ofstream fout; //创建一个对象
fout.open(“c:\tmp\clients.dat”,ios::out|ios::binary);
if(!fout){
}
文件写指针:
fout.tellp(); //取得写指针的位置
location = 10 ;
fout.seekp(location,ios::cur); //当前位置后置数location字节数 , location 可以是负数
读指针:
ifstream fin(“a.in”,ios::in|ios::ate); //打开文件定位到文件末尾
fin.tellg(); //取得读指针的位置
fin.seekg(location,ios::cur); //当前位置后置数location字节数 , location 可以是负数
字符文件读写:
while(fin>>x){
v.push_back(x);
}
fin.close(); //关闭文件。一定要关闭内存写入,io具柄
二进制文件读写:
istream & read(char *s,long n); //将文件读指针指向的地方的n个字节内容,读入内存地址s
istream & write(const char *s,long n); //将地址s处n个字节,写入文件中写指针指向的位置
int x=120;
fout.write((const char *) (&x),sizeof(int));
int y;
fin.read((char *)& y,sizeof(int));
//可以写入自定义结构体
Students s;
fout.write((const char *) & s,sizeof(s));
if(fin.read((char *)& s,sizeof(s))){
cout<<s.name;
};
fin.gcount(); //看上一次read读了多少字节
英文 字符串二进制和字符形式都能打开 ,写入方式相同
iofile(“a.in”,ios::in|ios::ate);
二进制文件更节省空间
int main(int argc ,char *argv[]){} //可执行文件输入 argc参数个数 argv参数列表指针 argv[0]开始
if(fin.get()){//每次读取一个字符 结束false
};
fout.put(); //每次写入一个字符
记事本只可以打开文本字符类型文件 ,不同操作系统换行不同 mac(\r) linux(\n);
endl 就是’\n’
ios::binary linux系统没有差别 Windows 差别很大
泛型程序设计
函数模版: 提高程序的可重用性
template 或 template
void Swap(T & x,T & y){
}
int i,j;
Swap(i,j); //可以直接使用,只定义一次template
遇到函数调用才会模版实例化
Swap(i,j); //两种调用方式
函数模版是可以重载的
template<class T,class T1>
void Swap(T & x,T1 & y){ //看参数表个数和类型
}
函数调用优先级:
1.普通函数 2.模版函数 3.经过类型转换能够匹配的函数
匹配模版函数时不会进行自动类型转换
类模版:
template<class T,class T1>或 template
class Arr{
T key;
T1 value;
Arr(T k,T1 v):key(k),value(v){};
boll operayor<(const Arr<T,T1> & p) const; //在类中将成员函数修饰为const表明在该函数体内,不能修改对象的数据成员而且不能调用非const函数
};
类内的成员函数也要带参数表
template<class T,class T1>
boll Arr<T,T1>::operayor<(T & x,T1 & y) const{ //看参数表个数和类型
}
同一个类模版的两个模版类是不兼容的。
函数模版可以作为类模版的成员:
类模板与函数模版的数据类型不同
类模版可以有非类型参数
template<class T,int size>
T array[size]; //类内部使用
调用:Arr<int,50> a3;
类模版可以从类模版中派生:
类模版与友元:
类模版与静态成员:静态成员要声明下
int A::b = 8;
//必须在定义类的文件中对静态成员变量进行一次声明 //或初始化,否则编译能通过,链接不能通过
string类:
#include //包含字符串库函数的声明
string s1(“Hello”);
string s2(8,‘x’); //s2=xxxxxxxx s2等于8个x
string s1=“Hello”;
string s1=‘c’; //单引号字符初始化是错误的
string s1;
s1=‘c’; //赋值可以,初始化不可以
s1.length(); //看字符串长度
cin>>s1; //支持流读取运算符
getline(cin,s1);
s2=s1; 可以相互赋值
s3.assign(s1,1,3);//以下标为1的字符开始复制3个字符
s4[2]=s1[5]; //单个字符赋值 s1.at(5);和[]区别,这个越界会返回异常,也可以按字符访问
s1 +=s2;两个字符串连接在一起
s2.append(s1,3,s1.size()); //从下标3开始添加s1.size()个字符 如果没有那么多,就拼接到最后一个为止
比较string:
== ,>,>=
s1.compare(s2); // ==输出0 s1>s2 输出1 小于输出 -1
成员函数:
s2=s1.substr(4,5); //取子串 取s1下标为4开始的5个字符
s1.swap(s2); //交互两个字符的内容
s1.find(“xl”,2); //在s1中下标2开始查找第一个"xl" 存在返回下标, 不存在返回 string::npos
s1.rfind(“xl”); //从后往前找
s1.find_first_of(“abcd”);// 返回"abcd"中任何一个字符第一次出现的位置
s1.find_last_of(“abcd”);// 返回"abcd"中任何一个字符最后一次出现的位置
s1.find_first_not_of(“abcd”);// 返回不在"abcd"中任何一个字符第一次出现的位置
s1.find_flast_not_of(“abcd”);// 返回不在"abcd"中任何一个字符第一次出现的位置
s1.erase(5); //删除下标5及以后的字符
s1.replace(2,3,“hahaha”,1,2); //将s1中下标2开始的3个字符替换成"hahaha" 下标1开始的2个字符
要替换特定字符先找到节点,在调用替换
s1.insert(2.s2,5,3);// 将s2中下标5开始的3个字符插入s1下标2的位置
s1.c_str(); //返回传统的const char * 类型的字符串,以\0 结尾
s1.data(); //返回传统的 char * 类型的字符串,以\0 结尾 ,可以修改
//字符串的流处理
#include //包含字符串库函数的声明
string input(“aa bb”);
istringstream inputString(input); //创建一个字符输入流类 类似cin。先入先出,出一次这个流里就没有了
inputString>>str1>>str2;
ostringstream outString;
outString<<“dd”<<“cc”;
cout<<outString.str();
STL模版库:
容器:可容纳各种数据类型的通用数据结构,是类模版
迭代器:可用于依次存取容器中的元素,类似于指针
算法:用来操作容器中的元素的函数模版
sort();find(); //算法本身与他们操作的数据的类型无关,简单数组到复杂容器都可以。 数组也被认为是容器
int array[10]; int * 类型的指针可以作为迭代器 sort算法可以用于该容器上,对其进行排序
sort(array, array+7);将前7个排序
容器概述:
可以用与存放各种类型的数据,是类模版
1)顺序容器:容器的顺序并没有排序,元素的插入位置和元素的值无关
vector 变长数组(向量)、deque 双向队列、list 双向链表
#include
动态数组,元素在内存中连续存放、随机存取n(1)时间复杂度、删除和添加中间是o(n),尾部删除和添加大部分都是o(1)
vector v;
vector v2(3,100); //v2 有4个元素,都是100
v.insert(v.begin()+1,13);
vector< vector > v2(3); //二维数组 v2有3个元素,每个元素都是vector
v[2].push_back(3);在第2行增加一个元素3.
cout<v[2][1]; //访问2行1列
#include
双向队列,元素在内存中连续存放、随机存取n(1)时间复杂度、删除和添加中间是o(n),首尾两端删除和添加大部分都是o(1)
所以适用 vector的都适用deque
push_front :在容器添加新元素到前面
pop_front:删除容器最前面的元素
#include
双向链表,元素在内存中不连续存放、不支持随机访问、在任何位置增删元素都能在常数时间完成,找到了对应插入的位置
push_front :在容器添加新元素到前面
pop_front:删除容器最前面的元素
list不支持STL的sort排序,可以调用list自身的sort排序
remove 删除和指定值相等的所有元素
unique 删除所有和前一个元素相同的元素(想要元素不重复,可以先sort排序在unique)
merge 合并两个链表,并清空被合并的那个
reverse 颠倒链表
splice 在指定位置前面插入另一个链表一个或多个元素,并在另一个链表删除被插入的元素
typename list::iterator iter ; //在函数模版里定义不确定的迭代器,要加typename
重载< 可以内部排序
重载== 可以删除数据和查找 或写入类的构造函数一致
2)关联容器
set、multiset 集合 (multiset可以存在相同元素)、map、multimap 字典
#include
Stl中平衡二叉树
必须重载< 才能比大小 定义为友元 要不不可以访问其他对象的私有变量
multiset st; //可重复的集合 平衡二叉树实现,元素类型T 从小到大排
st.insert(a[2]) 添加
equal_range 同时查找上下界 返回值为pair<T1,T2> first 为st.lower_bound ,second 为upper_bound
Count 等于某个值的个数
multiset::iterator i; //遍历容器需要迭代器 类似于指针 可以++ 和== 不可以比较,不可加减整数
st.begin(); 返回值是st中头一个元素的迭代器 multiset::iterator
st.end(); 返回值是st中最后一个元素的迭代器 multiset::iterator
st.find 查找 返回值是查到的迭代器 找不到就返回st.end() 查到的含义为不是x必须在y前面和y必须在x前面都不成立
it = st.lower_bound(13); //返回最靠后的迭代器it 使[st.begin(),it)中的元素都在13前面。
it = upper_bound(13); //返回最靠前的迭代器it 使[it,st.end())中的元素都在13后面。
st.erase(it) 删除迭代器it指向后面的元素
multiset 可以自定义排序规则
multiset<int,greater > st; //从大到小排
multiset<Student,Rule1 > st2; //自定义排序 结构T
set st; //不可重复的集合. 重复的意思x必须在y前面和y必须在x前面都不成立,则x与y重复。set插入元素可能不成功
set::iterator i; //迭代器
for(i=st.begin();i!=st.end();++i){
cout<< *i;
}
result=st.insert(2); /result 为pair<set::iterator,bool>
if(result.second){
//result.second表示插入成功 ,如果不成功 result.first 会指向已经存在的元素
}
pair<T1,T2> //模版 等价于下面的结构体。
struct ={
T1 first; //关键字
T2 second; //值
}
pair<int,double> a;
#include
typedef //定义类型
typedef multimap<int,StudentInfo> MAP_STD; //写了MAP_STD 相当于 multimap<int,StudentInfo>
make_pair(st.se,st.info); //生成类型相似的对应pair实例
集合 multiset可以存在相同元素
#include
顺序容器和关联容器中都有的函数:
begin:元素的第一个迭代器
end:最后一个元素后面的迭代器 没有东西不能访问 判断是否到头
rbegin:返回容器最后一个元素的迭代器
rend:第一个元素前面的迭代器 没有东西不能访问 判断是否到头
erase:从容器中删除一个或几个元素
clear:从容器中删除所有元素
顺序容器常用的成员函数:
front:返回容器第一个元素的引用
back:返回容器最后一个元素的引用
push_back :在容器尾部添加新元素
pop_back:删除容器尾部元素
erase:删除迭代器指向的元素,或删除一个区间返回被删除元素后面的元素的迭代器
迭代器:
可用于依次存取容器中的元素,类似于指针
有const和非const两种
通过非const可以修改其指向的元素
定义
容器类类名::iterator iter ;
vector v;
vector::iterator iter ;
vector::const_iterator iter ;
vector::reverse_iterator r ; //反向迭代器 r++ 就是向前 反向的
iter = v.begin();
iter !=v.end();
r != v.rbegin();
r !=v.rend();
++iter;
- iter ; //可以访问一个迭代器指向的元素
可以进行++操作,到达最后一个元素后面,此时在使用会出错,类似使用NULL的指针一样
双向迭代器:
包含基础操作
两个迭代器不能进行比较、不能随机访问[]下括号
可以 p-- 使p指向容器的上一个元素
p=p1; //进行赋值操作
随机访问迭代器:
包含所有双向迭代器的操作
p +=i; //将p向后移动i个元素
P +i;//指向p后面第i个元素的迭代器
p[i];// 直接访问p后面第i个元素的引用 p[i] = *(p+i);
p1<=p2 //p1所指向的元素排在p2前面
容器 容器上迭代器类别
vector 变长数组(向量)、deque 双向队列:随机访问迭代器
list 双向链表、set、multiset 集合 (multiset可以存在相同元素)、map、multimap 字典 :双向迭代器
stack 栈、queue 队列 、priority_queue 优先级队列 :不支持迭代器
算法:
#include
函数模版:查找、排序
算法通常通过迭代器进行操作容器元素:可以操作容器的一个局部区间、一般需要两个参数,起始元素的迭代器。终止元素后面一个元素的迭代器
容器往往会进行查找或排序,一般会对放入容器的对象所属的类,重载==和<
有的算法会返回一个迭代器:如find();返回指向该元素的迭代器
有的算法,如sort、binary_search 需要通过随机访问迭代来访问容器中的元素,那么list以及关联容器就不支持该算法了
算法可以处理容器,也可以处理普通数组
变值算法不能改变关联容器的值,可以会破坏关联容器的有序性
remove 删除之后返回可用后的一个迭代器,可能后面还有值需要复制下
reverse 颠倒区间的前后顺序
next_permutation 求下一个排列
bitset 可以生成二进制标志位数组 可以直接访问
min 两个对象中较小的 max
min_element 求区间的最大值 max_element
for_each 对区间中每个元素都做某种操作
cont() 计算等于某个值的个数 cont_if() 复合某种条件的元素个数
find find_if find_end find_first_of
adjacent_find 在区间中寻找第一次出现连续两个相等元素的位置
Search equal 相等 minsmatch 返回第一个不相等的元素
vector v;
vector::iterator iter ;
iter = find(v.begin(),v.end(),3); //查找 返回值是查到的迭代器 找不到就返回last 用判断 时间复杂度O(n)的
find(array, array+4,3); /数组查找
算法提到的区间都是左闭右开的[)
关联容器内部的元素是从小到大排序的
有些算法要求操作区间从小到大有序,称为"有序区间算法"
如:binary_search
从小到大可以自定义比较,缺省的情况下默认就是数值大小
相等的概念:
在未排序的区间算法,为 如find
在排序区间算,含义为不是x必须在y前面和y必须在x前面都不成立 两个<小于都不成立 如binary_search和关联容器自身的find
函数对象:
如果一个类重载了(),则该类的对象被称为函数对象
double operator()(int a,int b){
}
A a1;
A1(2,3); //这个类就可以直接当函数使用
accumulate 累加函数,实现用的函数对象
accumulate(v.begin,v.end,A(3)) //通过函数对象调用可以把函数更改的值传递给对应调用函数的地方
函数对象类模版
#include
greater() 比较大小 x>y. //降序排列时,运用这个函数对象参数 重载> 实现类的比较
类名() 就会生成一个类的临时变量
STL 标准模版库
要使用其中的算法
include
sort(a+2,a+5,greater()) 排序 对基本类型的数组从小到大排序. 从0开始 [2,5) 带greater() 实现基本数据类型从大到小排序 第三个参数为排序规则
对自定义结构T排序
sort(a+2,a+5,排序规则结构名())
struct Bijiao{
bool operator()(const T & a1 , const T & a2){
//如果a1应该在a2前面就返回true; 其他返回false
}
}
binary_search(a+2,a+5,12); //在排好序的数组a上二分查找基本数据类型12
binary_search(a+2,a+5,12,排序规则结构名()); // 在自定义的排序规则,对T类型进行二分查找。等于的含义为不是大于或小于。查找时候的规则和排序时候的规则一致
T * lower_bound(a+2,a+5,12); //在对元素类型T排好序的基本类型数组,进行查找[2,5)之间下标最小的大于等于值。
T * lower_bound(a+2,a+5,12,排序规则结构名()); //在对元素类型T排好序的数组,进行查找[2,5)之间下标最小的可以排在值12后面的元素,找到指向该元素,没找到指向a+5元素。
T * upper_bound(a+2,a+5,12); //在对元素类型T排好序的基本类型数组,进行查找[2,5)之间下标最小的大于值。
T * upper_bound(a+2,a+5,12,排序规则结构名()); //在对元素类型T排好序的数组,进行查找[2,5)之间下标最小的必须排在值12后面的元素,找到指向该元素,没找到指向a+5元素。
C++新特性
统一初始化方法:
map<int,string>{{1,‘a’},{2,‘b’}}; //统一用{}初始化
成员变量可以有默认的初始值
int m=11;
auto i=100; //定义变量,根据变量类型匹配 成员变量时必须初始化
->decltype(x+y)指定返回值类型
decltype() 返回表达式的类型 decltype((x)) 返回x的引用
include
智能指针 shared_ptr 类模版 不用手动delete 使用方式跟指针类似 不能托管数组的指针
不能用reset§赋值
空指针 nullptr
int * p =nullptr;
基于范围的循环
for(auto & e:ary){
e+=10;
}
右值引用和move语义 移动构造函数 联合使用可以优化效率,避免深拷贝
A && r=A(); 可以右值引用临时变量。可不可以取地址判断左值和右值
move(a); 左值引用返回右值。
#include <unordered_map>
哈希表无序容器:
用法和map相同 区别在于时间效率比map更高 几乎都是常数
需要更多的内存空间
#include
正则表达式: regex reg("b.?p.");
regex_match(“bopggk”,reg); 输出1就是找到 0就是没找到
.? 任意字符出现一次
. 任意字符出现多次
\d{3} 数字出现3次
- 一次或若干次
Lambda 表达式
在定义函数对象类或函数的时候、调用时在定义 不用单独定义
[](double a ,double b){ return a+b;}(1.2,2.5);
auto ff=[=,&y,&z](int n){
cout <<x;
y++; z++;
return n*n;
}
ff(15);
sort(a,a+4,[](int x,int y)->bool{return x%10<y%10});
function<int(int)> fib=[&fib](int n){return n};
递归调用不能用 auto
强制类型转换:
static_cast 用来低风险的转换 查找出错处的转换字符 不能用于指针之间的转换
static_cast(3.14);
reinterpret_cast<int&>(a); 指针和引用的转换
const_cast const对应的指针转换为非const
dynamic_cast 多态基类指针或引用转换为派生类
异常处理
Try catch 处理异常 按顺序匹配
try{
throw -1; //抛出异常try立即停止运行
}
catch(int e){
cout<<‘catch(int)’;
}
catch(…){ //可以捕获任何的异常
cout<<‘catch(…)’;
throw -2; //返回调用来源异常
}
抛出异常如果没有被catch住程序就会崩溃退出
exception c++定义的异常
#include
e.what(); //输出异常信息