C++看这个
一、数据类型
1、const关键字
1、const以常量初始化,修饰的只读变量,变量值会存放在符号常量表中,不会立即给data开辟空间,取地址时才开辟空间。
const int data=100;//以常量初始化
int *p = (int *)&data;
*p=200;
cout<<"*p="<<*p<<endl;//200
cout<<"data="<<data<<endl;//100
2、const以变量初始化,修饰的只读变量,会立即开辟空间。
int data1=1000;
const int data2=data1;//以变量初始化
int *p1= (int *)&data2;
*p1=2000;
cout<<"*p1="<<*p1<<endl;//2000
cout<<"data2="<<data2;//2000
3、const 修饰常量指针,指针的指向可以修改,指向的值不可以修改
int a = 10;
int b = 20;
const int * p = &a;
*p=11;//错误,指向的值不能修改
p=&b;//正确,指向可以修改
4、const修饰指针常量,指针的指向不可以修改,指向的值可以修改
int a = 10;
int b = 20;
int * const p = &a;
*p=11;//正确,指向的值可以修改
p=&b;//错误,指向不可以修改
2、volatile关键字:强制从内存访问
3、typedef 取别名。
typedef int INT;
INT data=1;
typedef int ARRAY[3];
ARRAY arr={"1","2","3"}
typedef int *MYP;//指针类型
MYP p;
4、static 定义的静态变量,只在自己的{ }内有效; 静态函数 就近原则。
二、运算符
1./:除号
5 / 2 == 2
5 / 2.0 == 2.5
2.随机数
rang()函数取 最大数和最小数的差再加一的余数,再加上最小数
产生一个100~200的随机数
int a = rand()%101+100
产生一个a~z的随机字母。
char ch = rand()%26+'a';
#include <time.h>
void testrand(){
//获取随机数种子,time (NULL)获取当前时间
srand(time(NULL));
cout<<rand()<<endl;
}
3.遍历二位数组
void testArrylist02(){
int arr[3][3]={19,27,32,46,87,32,45,13,54};
float scoreavg[3]={0.0f};
for (int i=0;i<3;i++) {
int sum=0;
for (int j=0;j<3;j++) {
sum+=arr[i][j];
}
scoreavg[i]=sum/3;
cout<<scoreavg[i]<<endl;
}
}
三、指针
1、指针数组
数组的每个元素为指针
char *a[3];
2、数组指针
本质是指针变量,保存的是数组的首地址
int arr[5]={10,20,30,40,50}
int (*p)[5]=&arr;//p=&arr,*p=*&arr=arr,*(*p+2)=30
3、一维数组做函数参数,会被编译器优化成,指针变量
4、函数指针变量,使用typedef
int add(int x,int y){
return x+y;
}
void testadd(){
//给函数指针类型取别名
typedef int (*ADD)(int x,int y);
ADD p=NULL;
p=add;
cout<<p(10,20)<<endl;
}
四、字符串长度,复制
1.strlen和sizeof的区别
sizeof: 是运算符,以字节为单位,计算机占用内存大小,在编译时处理,处理的参数为类型,对象;
strlen: 是函数,计算字符串的长度,在运行时处理,处理的参数类型为指针,数组,并且要有字符串的头文件
2、字符串覆盖strncpy
//把d数组的前3位,覆盖到e数组的前3位。
char c[100]="12345";
char d[5]="6789";
strncpy(c,d,3);
cout<<c<<endl;//67845
3、字符串追加 strncat()
//把f数组的前3位追加到e数组。
char e[100]="12345";
char f[5]="6789";
strncat(e,f,3);
cout<<e<<endl;//12345678
4、字符串比较 strcmp()和strncmp()
/*
* >0:s1字符串>s2字符串
* <0:s1字符串<s2字符串
* =0:s1字符串=s2字符串
*/
//字符数组
char g[5]="abcd";
char h[5]="abcc";
int a=strcmp(g,h);
int num=strncmp(g,h,4);//比较前4位
cout<<num<<endl;
//字符串
string name1 = "zhangsan";
string name2 ="lisi";
int result=name1.compare(name2);
cout<<result<<endl;
cout<<sizeof (name2)<<endl;//24
cout<<name1.size()<<endl;//8
cout<<name2.length()<<endl;//4
五、按位运算
1.按位与
按位与&:有0为0,全1为1
2.按位或
有1为1,全0为0
3.按位异或and按位取反
~按位取反,0变1,1变0;
^按位异或相同为0,不同为1
六、C++基础知识
1、作用域
局部变量和全局变量同名,要在局部类或方法使用全局变量,加上 ::
2、命名空间
using声明
先找局部,再找全局,再到命名空间去找
3、结构体
C定义结构体变量需要加struct关键字,C++不需要
C结构体只能定义成员变量,C++既可以定义成员变量,还可以定义成员函数
4、引用
1、变量的引用
#include <iostream>
using namespace std;
void testyinyong(){
int a=10;
//给变量a 取别名b, &修饰变量为引用,b就是a 的引用(别名)
//系统不会为b开辟空间
int &b=a;//引用必须初始化
cout<<b<<endl;
}
int main()
{
testyinyong();
return 0;
}
2、数组的引用
//数组的引用
void testarray(){
int arr[3]={2,56,32};
int n=sizeof(arr)/sizeof(arr[0]);
int (&myarr)[3]=arr;
for(int i=0;i<n;i++){
cout<<(myarr)[i]<<endl;
}
}
3、指针的引用
//指针的引用
void testPointer(){
int num=10;
int *p=#
//指针p取别名
int* &myp=p;
cout<<"*p="<<*p<<endl;//10
cout<<"*myp="<<*myp<<endl;//10
}
4、函数的引用
//函数的引用
void Fun(int i){
cout<<i;
}
void (&myFun)(int a)=Fun;
int main()
{
myFun(2);
return 0;
}
5、引用作为函数的参数
//引用作为函数的参数
void Fun01(int &x,int &y){
int z=x;
x=y;
y=z;
}
int main()
{
int a=11;
int b=22;
Fun01(a,b);
cout<<"a="<<a<<" "<<"b="<<b<<endl;
return 0;
}
5、内联函数(编译阶段,函数体替换函数调用处,避免函数调用的开销)
1.在定义时用inline修饰,不能在声明的时候修饰
//声明函数
int add(int x,int y);
int main(){
int a=3;
int b=4
cout<<add(a,b);
}
//定义函数
inline int add(int x,int y){
return x+y;
}
2.内联函数注意实现
1.在内联函数定义的时候加inline修饰
2.类中的成员函数默认都是内联函数(不加inline也是内联函数)
3.有时候就算加上inlinet也不一定是内联函数(内联函数条件)
不能存在任何形式的循环语句
不能存在过多的条件判断语句
函数体不能过于庞大
不能对函数取地址
4.有时候不加inline修饰也有可能是内联函数。
5.内不内联由编译器决定.
6、#if,#endif
C++的条件编译预处理命令,必须以结束 #endif 指令与源文件中的每个 #if 指令。任意数量的 #elif 指令可以出现在 #if 和 #endif 指令之间,但是,最多一个 #else 指令允许。#else 指令,如果有,则必须是最后一个指令
有两种格式:
1:#ifdef 标示符
程序段1
#else
程序段2
#endif
表示:如果标示符已经被#define命令定义过,则编译程序段1,否则编译程序段2
2:#if 表达式
程序段1
#else
程序段2
#endif
表示:如果表达式为真,则编译程序段1,否则编译程序段2.
7、析构函数(当类中有指针成员,这个类必须写)
函数名和类名相同,函数名前加~,没有返回值类型,没有形参,不能被重载,当对象生命周期结束后,系统会自动调用析构函数,再释放内存空间。
#include <iostream>
#include <string.h>
using namespace std;
class Date{
public :
char *name;
public:
Date(){
name=NULL;
}
Date(char *str){
name =new char [strlen(str)+1];
strcpy(name,str);
}
~Date(){
if(NULL!=name){
delete [] name;
name=NULL;
}
}
};
int main()
{
Date stu("guoc");
cout<<stu.name;
return 0;
}
8.拷贝构造函数以及深拷贝(深拷贝括号里要用引用)
1.旧对象给新对象初始化时,就会调用拷贝构造函数
2.深拷贝(要用引用):为对象的指针成员申请独立的空间
3、qt处理返回对象时:外界对象接管函数内部返回的对象,没有拷贝构造提高了效率,不会创建匿名对象提高效率
#include <iostream>
#include <string.h>
using namespace std;
class Date{
public :
char *name;
public:
Date(){
name=NULL;
}
Date(char *str){
name =new char [strlen(str)+1];
strcpy(name,str);
}
//深拷贝(括号里要用引用)
Date(const Date &stu){
//为对象的指针成员申请独立的空间
name=new char [strlen(stu.name)+1];
strcpy (name,stu.name);
}
//析构函数(有指针成员时,必须写)
~Date(){
if(NULL!=name){
delete [] name;
name=NULL;
}
}
};
int main()
{
Date stu("Hello World!!!");
cout<<stu.name;
return 0;
}
9、对象成员以及初始化列表
#include <iostream>
using namespace std;
class A{
public:
int Aage;
public:
A(){
Aage=0;
}
A(int a){
Aage=a;
}
~A(){
}
};
class B{
public:
int Bage;
A guoc;//成员对象
public:
B(){
}
//初始化列表,成员对象,使用 对象名+()
B(int a,int b):guoc(a){
Bage=b;
}
~B(){
}
};
int main()
{
B xuanz(21,22);
cout<<"Aage="<<xuanz.guoc.Aage<<" Bage="<<xuanz.Bage<<endl;
return 0;
}
10.explicit关键字
explicit关键字,修饰构造函数是禁止构造函数进行隐式转换,一般是针对单参数构造函数,或一个参数,其他参数有默认值的构造函数
11、static关键字
1、static修饰的静态成员,属于类不属于对象(所有对象共享一份静态成员数据)。
2、在定义类时,静态成员就分配空间。
3、静态成员必须在类中定义,类外初始化, 当然啦类外则不用再加static关键字。
4、静态成员函数,只能操作静态成员变量;非静态成员函数,可以调用类的静态成员函数和静态变量(因为静态成员函数内部没有this指针).
#include <iostream>
#include <bitset>
using namespace std;
//static关键字
class A
{
public:
int x=10;
//静态成员类中定义
static int y;
};
//静态成员类外初始化, 类外则不用再加static关键字
int A::y=20;
void testStatic(){
cout<<A::y<<endl;
A guoc;
cout<<guoc.y;
}
int main()
{
testStatic();
return 0;
}
12.const修饰成员函数(只读状态)
在成员函数体内,不可以修改本类中任意的普通成员变量,当成员变量类型为mutable时除外
13、友元
1.使用friend关键字在一个类或函数中,声明一个类或函数,那么这个类或函数可以直接访问这个类或函数的私有数据。
2.使用规则:将作为友元的类或函数的声明放在最前面,友元的初始化实现放在最后面。
3.注意事项:友元关系不能被继承;友元的关系是单向的;并且不具有传递性;
14、全局函数重载
1、输出运算符重载 <<
#include <iostream>
#include <string>
using namespace std;
class Person{
friend ostream& operator<<(ostream &out,Person &ob);
private:
int num;
string name;
float score;
public:
Person(){}
//有参构造函数,初始化列表
Person(int num,string name,float score):num(num),name(name),score(score){}
};
//重载全局函数,这里重载 operator<<
ostream& operator<<(ostream &out,Person &ob){
out<<"学号:"<<ob.num<<" 姓名:"<<ob.name<<" 分数:"<<ob.score<<endl;
return out;
}
int main(int argc, char *argv[])
{
Person cat(002,"tom",88.9);
cout<<cat;
cout<<cat<<endl;
return 0;
}
2、重载 operator>>
//重载 operator>>
istream& operator>>(istream &in, Person &ob){
in>>ob.num>>ob.name>>ob.score;
return in;
}
15、智能指针(智能指针,相当于定义一个公共的,会释放内存的指针)
#include <iostream>
using namespace std;
class Date{
public:
void func(){
cout<<"智能指针"<<endl;
}
};
//智能指针,相当于定义一个公共的,会释放内存的指针
class SP{
public:
Date *p;
public:
SP();
SP(Date *dp){
this->p=dp;
}
~SP(){
delete p;
}
//重载->运算符
Date* operator->(){
return p;
}
//重载*
Date& operator*(){
return *p;
}
};
int main()
{
//生成一个SP类的指针对象,指向Date对象
SP sp=new Date;
sp->func();
(*sp).func();
return 0;
}
16、继承
1.继承中的构造顺序,注意看是父类的类名还父类的对象名
2、继承中同名处理
(1)子类和父类的同名处理:添加作用域。
(2)子类和父类的同名成员处理:子类默认优先访问子类同名成员,加作用域才会访问父类同名成员。
3、菱形继承,虚继承
17、多态
1、静态多态(编译阶段,早绑定):函数重载,运算符重载,重定义。
2、动态多态(运行阶段,晚绑定):虚函数。
3、虚函数(是被virtual修饰的成员函数,会产生一个虚函数指针vfptr,指向虚函数表,如果这个类没有涉及到继承,这时虚函数表记录的就是当前虚函数的入口地址)。
4、
重载:无继承,同一作用域,参数的个数不同、顺序不同、类型不同都可重载。
重定义:有继承,子类重定义父类的同名非虚函数(参数顺序,个数,类型可以不同),子类一旦重定义了父类的同名函数(不管参数是否一致),子类中将屏蔽父类所有的同名函数。
重写(覆盖):有继承,子类 重写父类的虚函数,返回值类型,函数名,参数顺序,个数,类型必须一致。
5、纯虚函数
定义方式
class Animal
{
public:
Animal() {}
//纯虚函数
virtual void speak(void)=0;
};
- 一旦这个类有纯虚函数,那么这个类,就是抽象类
2.抽象类,不能实例化对象
3.抽象类必须被继承,同时子类 必须被重写父类所有纯虚函数,否则子类也是抽象类。
6、虚析构函数 和 纯析构函数(作用上没什么区别,都是通过父类指针,来释放整个子类空间,定义方式不同, 而且这个类有纯析构函数,那么这个类,就是抽象类)
1、虚析构: 通过父类指针,释放整个子类空间。
#include <iostream>
using namespace std;
//虚函数
class Animal02
{
public:
Animal02() {
cout<<"Animal02构造实现了"<<endl;
}
//虚析构:通过父类指针,释放整个子类空间
virtual ~Animal02(){
cout<<"Animal02析构实现了"<<endl;
}
virtual void speak(void){
cout<<"叫了一声"<<endl;
}
};
class Cat:public Animal02
{
public:
Cat(){
cout<<"Cat构造实现了"<<endl;
}
~Cat(){
cout<<"Cat析构实现了"<<endl;
}
void speak(void){
cout<<"喵喵"<<endl;
}
};
void animalSpeak(Animal02 *p){
p->speak();
delete p;
}
int main()
{
//虚函数
animalSpeak (new Cat);//通过父类指针,调用子类函数
return 0;
}
2.纯虚析构函数:处理各类的回收工作,不能被继承,并且得有函数体,以及要在类外实现
#include <iostream>
using namespace std;
class Animal01
{
public:
Animal01(){
cout<<"Animal01构造 实现了"<<endl;
}
//纯虚析构,必须在类外实现
virtual ~Animal01()=0;
//纯虚函数
virtual void speak(void)=0;
};
//纯虚析构的类外实现
Animal01::~Animal01(){
cout<<"Animal01纯虚析构,实现了"<<endl;
}
class Dog:public Animal01
{
public:
Dog(){
cout<<"Dog构造实现了"<<endl;
}
~Dog(){
cout<<"Dog析构实现了"<<endl;
}
virtual void speak(void){
cout<<"叫了汪汪!"<<endl;
}
};
int main()
{
//纯虚函数部分,包括虚析构
Animal01 *a=new Dog;
a->speak();
delete a;
return 0;
}
18、模板
1、函数模板
#include <iostream>
using namespace std;
//T只对当前下面的函数有效
template <typename T>
void swapAll(T &a,T &b){
T tmp = a;
a=b;
b=tmp;
return;
}
void swapAll(int &a,int &b){
int tmp = a;
a=b;
b=tmp;
return;
}
//第二部分
class Stu{
friend ostream& operator<<(ostream &out,Stu stu);
private:
string name;
public:
Stu(){}
Stu(string name){
this->name=name;
}
};
template <typename T>
void myPrint(T a){
cout<<a<<endl;
}
ostream& operator<<(ostream &out,Stu stu){
out<<stu.name<<endl;
return out;
}
int main()
{
int x=4,y=5;
swapAll(x,y);//会优先使用 普通函数
//函数调用时,根据实参类型,会自动推到T的类型
swapAll<>(x,y);//加 <> 会优先使用 函数模板
//cout<<"x= "<<x<<" y= "<<y<<endl;
//第二部分,对于 T 为数组或其他自定义类,可能导致运算符不能识别
Stu guoc("郭超");
myPrint(guoc);
return 0;
}
2、类模板
19、类型转换
1、static_cast 静态类型转换(支持3中:基, 上 ,下)
#include <iostream>
using namespace std;
class Father{
public:
double a=3.14;
};
class Son:public Father{
public:
//基本类型转换:支持,安全,
int num = static_cast<int>(a);
};
int main()
{
//1.static_cast 静态类型转换
//上行转换:支持,安全
Father *p = static_cast<Father *>(new Son);
cout<<p->a<<endl;
//向下转换:支持,不安全
Son *sp = static_cast<Son *>(new Father);
cout<<sp->num<<endl;
cout<<sp->a<<endl;
return 0;
}
2、dynamic_cast 动态类型转换(支持1种:上)
//2.dynanic_cast 动态类型转换
//上行转换:支持
Father *fp = dynamic_cast<Father *>(new Son);
cout<<fp->a<<endl;
3、interpret_cast 重新解释转换(支持3种:基指针 ,上 ,下)
//3、reinterpret_cast重新解释转换
//1.基本类型:不支持
//int num =reinterpret_cast<int>(3.14f);
//2.基本指针类型:支持
float *q;
int *fq = reinterpret_cast<int *>(q);
//3.向上转换:支持
Father *fp2 = reinterpret_cast<Father *>(new Son);
//4.向下转换:支持‘
Son *sp2 = reinterpret_cast<Son *>(new Father);
4、const_cast 常量转换(两种切换:指针,引用)
1、将const 修饰的 指针,引用 转换为 非const修饰的
//1、将const 修饰的 指针和引用,转换为 非const修饰的
int cc2[3]={10,20,30};
const int *cp1=cc2;
int *cp2= const_cast<int *>(cp1);
const int &ob1=cc2[1];
int &ob2=const_cast<int &>(ob1);
2、将非const 修饰的 指针,引用 转换为 const修饰的
//2、将非const 修饰的 指针,引用 转换为 const修饰的
int cc3=20;
int *cp3=&cc3;
const int *cp4= const_cast<const int *>(cp3);
int &ob3=cc3;
const int &ob4=const_cast<const int &>(ob3);
20、异常(catch()里要用引用)
1、 栈解旋:从进入try块起,到异常抛出前,这期间在栈中所有构造的对象,都会自动析构。
2、 异常的多态
#include <iostream>
#include <exception>
using namespace std;
//异常的多态
class BaseException{
public:
virtual void printError(){}
};
//空指针异常
class NullPointerException:public BaseException{
public:
virtual void printException(){
cout<<"空指针异常!!"<<endl;
}
};
//越界异常
class OutofRangeException:public BaseException{
public:
virtual void printError(){
cout<<"越界异常!!!"<<endl;
}
};
void dowork(){
throw OutofRangeException();
}
};
int main()
{
try {
dowork();
} catch (BaseException &e) {
e.printError();
}
return 0;
}
3、如何编写自己定义的异常
注意:要基于标准异常基类,编写自己的异常类,继承exception
#include <iostream>
#include <exception>
using namespace std;
//编写自己的异常,基于标准异常基类,编写自己的异常类
class NewException:public exception
{
private:
string msg;
public:
NewException(){}
NewException(string msg){
this->msg=msg;
}
//重写父类的what方法
virtual const char* what() const throw()//防止父类在子类前抛出标准异常
{
//将string类型转换成char*
return this->msg.c_str();
}
~NewException(){}
};
int main()
{
//自己的异常
try {
throw NewException("自己的异常");
} catch (exception &e) {
e.what();
cout<<e.what();
}
return 0;
}
七,STL容器
1.STL容器六大组件
1.容器:存放数据
2.算法:操作数据(质算法:更改区间元素内容,如:拷贝替换删除。
非质算法:没更改区间元素内容,如:查找,遍历,计数等)
3.迭代器:算法通过迭代器操作容器数据
4.仿函数:为算法提供更多策略
5.适配器:为算法提供更多参数接口
6.空间配置器:为算法和容器 动态分配、管理空间.
2.StringAPI (注意一定要有string的头文件)
1.string 的 声明 和 查询
#include <iostream>
//注意要有string的头文件
#include <string>
using namespace std;
//声明和查询
void defineSelect(){
string guoc="hello world!!!";
cout<<guoc[3]<<" "<<guoc.at(3)<<endl;
guoc[6]='W';
guoc.at(0)='H';
cout<<guoc<<endl;
//注意:[]越界不会抛异常, at方法越界会抛出异常
try {
throw guoc.at(20)='A';
} catch (exception &e) {
cout<<e.what();
}
}
int main()
{
defineSelect();
return 0;
}
2、字符串 的 连接(直接用+符号,或者append函数( append:附加,增加))
#include <iostream>
//注意要有string的头文件
#include <string>
using namespace std;
void joint(){
string str1="hello";
str1+="world";
cout<<str1<<endl;
string str2="guoc say:";
str2+=str1;
cout<<str2<<endl;
string str3="abcd";
string str4="ABCD";
str3.append(str4);//4追加到3末尾
cout<<str3<<endl;//abcdABCD
str3.append("efgh",2);//efgh前两位追加到3
cout<<str3<<endl;//abcdABCDef
string str5="abcd";
string str6="ABCD";
str5.append(str6,2,3);//6的下标从2到3,追加到5上
cout<<str5<<endl;//abcdCD
}
int main()
{
joint();
return 0;
}
3、string 字符串的比较 ( 直接> ,< , =, 或者 compare()函数 )
1.= 返回0
2.> 返回1
3.< 返回-1
#include <iostream>
//注意要有string的头文件
#include <string>
using namespace std;
void testCompare(){
string str7="haha";
string str8="abcd";
int outcome1=str7.compare(str8);
int outcome2=(str7>str8);//重载了:= > <
cout<<outcome1<<endl;
cout<<outcome2<<endl;
}
int main()
{
testCompare();
return 0;
}
4、截取,提取子字符串( substr()函数 )
#include <iostream>
#include <string>
using namespace std;
void extract(){
string str9="111:222:333:444";
int pos=0;
while(1)
{
int ret=str9.find(":",pos);
if(ret<0){
cout<<"无符合要求"<<endl;
break;
}
string tmp =str9.substr(pos,ret-pos);
cout<<tmp<<endl;
break;
}
}
int main()
{
extract();
return 0;
}
5、字符串的插入和删除( inster()函数 和 erase()函数 )
#include <iostream>
#include <string>
using namespace std;
void insertDelete(){
string str10="limeng";
str10.insert(2,"meng666");//从下标为2,开始插入
cout<<str10<<endl;//limeng666meng
str10.erase(6,3);//从 下标为6开始,删除3个字符
cout<<str10<<endl;
}
int main()
{
insertDelete();
return 0;
}
7、字符串的类型转换:string需要成员函数 c_str() , 转成 char *
#include <iostream>
#include <string>
using namespace std;
void cstyleChange(){
//string需要成员函数c_str(),转成char *
string str11="limengmeng";
char *name1=const_cast<char *>(str11.c_str());
//为什么只输出指针名就可以打印出整个字符串?
//因为打印时遇到 \0 ,才会自动结束
cout<<name1<<endl;
cout<<&name1<<endl;//虽然指针名就代表的是地址,但在输出地址时需要加&
//char *转成string(默认支持)
const char *name2="guoc";
string str12=name2;
cout<<str12<<endl;
}
int main()
{
cstyleChange();
return 0;
}
3、Vector容器
1、Vector容器介绍:是单向线性连续内存空间,动态增加大小(未雨绸缪机制)是原空间大小满载后,会开辟一个原空间2倍大小的新空间,将原空间内容拷贝到新空间,再释放原空间内存。
#include <iostream>
//注意头文件
#include <vector>
using namespace std;
void test01(){
vector<int> v1;
//预留50的空间大小
v1.reserve(50);
cout<<sizeof (v1)<<endl;
cout<<"容量"<<v1.capacity()<<" 大小:"<<v1.size()<<endl;
//定义一个迭代器
vector<int>::iterator it;
int i=0;
//记录开辟新空间次数
int count=0;
for (i;i<100;i++) {
//尾插法
v1.push_back(i);
if(it != v1.begin()){
count++;
cout<<"第"<<count<<"次开辟的空间容量"<<v1.capacity()<<endl;
//重新指向新空间
it=v1.begin();
}
}
//遍历一下
for (;it!=v1.end();it++) {
cout<<*it<<" ";
}
cout<<endl;
}
int main()
{
test01();
return 0;
}
2、vector容器的 初始化,交换,空,定义容器空间长度的一些函数
#include <iostream>
#include <vector>
using namespace std;
//定义一个遍历函数,方便下面调用
void itPrint(vector<int> v){
vector<int>::iterator it=v.begin();
for (;it!=v.end();it++) {
cout<<*it<<" ";
}
cout<<endl;
}
//vector容器的初始化,交换,空,定义容器空间长度的一些函数
void implItPrint(){
vector<int> v2(5,100);//初始化int型的5个100在容器中
itPrint(v2);
vector<int> v3(v2.begin(),v2.end());//将v2拷贝到v3
vector<int> v4;
//v4=v2;//一样的效果,直接赋值
//v4.assign(v2.begin(),v2.end());
v4.assign(10,100);//初始化10个100
v4.swap(v3);//交换函数
itPrint(v3);
itPrint(v4);
cout<<v4.size()<<endl;//输出v4中元素的个数
//v4.empty(),为空才正确
if(v4.empty()){
cout<<"v4确实为空"<<endl;
}else {
cout<<"v4不为空"<<endl;
}
vector<int> v5;
v5.assign(5,30);
v5.resize(10);//重新指定容器长度,为10,并且多出部分,补0,超出部分删除。
//v5.resize(10,2);//超出部分补2
v5.reserve(20);//预留容器长度为20个元素,多出的部分未被初始化,也不可访问
}
int main()
{
implItPrint();
return 0;
}
3、vector容器的插入,查询,修改,删除
#include <iostream>
#include <vector>
using namespace std;
//vector容器的插入,查询,修改,删除
void testCRUD(){
vector<int> v6;
v6.push_back(10);
v6.push_back(20);
v6.push_back(30);
v6.push_back(40);
v6.push_back(50);
cout<<"v6的头元素:"<<v6.front()<<" v6的尾元素"<<v6.back()<<endl;
//用[]越界不报错,用at越界会抛异常
cout<<v6[2]<<" "<<v6.at(2)<<endl;
v6.at(2)=300;
v6[3]=400;
v6.pop_back();//删除尾部元素
itPrint(v6);//10 20 300 400
v6.insert(v6.begin()+1,3,20);//在迭代器指向位置+1处,插入3个20
itPrint(v6);//10 20 20 20 20 300 400
v6.erase(v6.begin()+1,v6.begin()+4);//删除迭代器指向位置+1(不包括)到4(包括)处
itPrint(v6);//10 20 300 400
v6.clear();//清空容器
}
int main()
{
testCRUD();
return 0;
}
4、巧用swap函数收缩内存空间
#include <iostream>
#include <vector>
using namespace std;
//巧用swap函数收缩内存空间
void swapShrink(){
vector<int> v7;
v7.reserve(100);
v7.push_back(10);
v7.push_back(20);
v7.push_back(30);
v7.push_back(40);
v7.push_back(50);
cout<<"v7的容量:"<<v7.capacity()<<" v7长度:"<<v7.size()<<endl;
//v7.resize(4);//resize函数只能修改长度大小,不能修改容量
vector<int> (v7).swap(v7);
cout<<"v7的容量:"<<v7.capacity()<<" v7长度:"<<v7.size()<<endl;//4,4
}
int main()
{
swapShrink();
return 0;
}
5、vector容器的 嵌套
//vector容器嵌套
void nestVector(){
vector<int> v8(3,10);
vector<int> v9(3,100);
vector<int> v10(3,1000);
//定义一个总容器,存放容器
vector<vector <int>> vsum;
vsum.push_back(v8);
vsum.push_back(v9);
vsum.push_back(v10);
//第一种遍历:
// vector<vector<int>> ::iterator it=vsum.begin();
// for (;it!=vsum.end();it++) {
// vector<int> :: iterator mit=(*it).begin();
// for (;mit!=(*it).end();mit++) {
// cout<<(*mit)<<" ";
// }
// cout<<endl;
// }
//第二种遍历:
for (auto e:vsum ) {
//用的是前面定义的迭代器
itPrint(e);
}
}
6、自定义类的vector容器,以及 sort()排序( 要加上算法头文件)
#include <iostream>
#include <vector>
//算法头文件
#include <algorithm>
using namespace std;
//sort函数进行排序
void sortVector(){
vector<int> v11;
v11.push_back(10);
v11.push_back(30);
v11.push_back(50);
v11.push_back(20);
v11.push_back(40);
sort(v11.begin(),v11.end());
itPrint(v11);
}
//自定义类
class Person{
friend void printPerson(vector<Person> &v);
friend bool comparePerson(Person &ob1,Person &ob2);
private:
int num;
string name;
public:
Person(){}
Person(int num,string name){
this->num=num;
this->name=name;
}
};
//自定义遍历函数
void printPerson(vector<Person> &v){
vector<Person> ::iterator itp=v.begin();
for (;itp!=v.end();itp++) {
cout<<(*itp).num<<" "<<(*itp).name<<endl;;
}
}
//自定义比较规则
bool comparePerson(Person &ob1,Person &ob2){
return ob1.num<ob2.num;
}
void custom(){
vector<Person> vp;
vp.push_back(Person(002,"权总"));
vp.push_back(Person(003,"轩总"));
vp.push_back(Person(001,"煤总"));
//打印vp中的Person
printPerson(vp);
//指定排序规则
sort(vp.begin(),vp.end(),comparePerson);
printPerson(vp);
}
int main()
{
custom();
return 0;
}
4、deque容器
1、双向开口的连续线性空间,至少逻辑上看是连续的,两端都可以做插入删除操作,并且deque没有容量的概念,是由一段一段的定量的连续空间构成,如果有必要在deque前端或尾端增加新空间,便添加一段连续定量的空间,串接在deque的头部或尾部。deque是分段连续内存空间,通过中央控制,来维持整体连续的我假象
2、deque容器的函数调用(与vector差不多)
#include <iostream>
#include <deque>
using namespace std;
void printDequeInt(deque<int> &d){
deque<int>::iterator it=d.begin();
for(;it!=d.end();it++){
cout<<(*it)<<" ";
}
// for (int e:d) {
// cout<<e<<" ";
// }
cout<<endl;
}
void deque01(){
deque<int> d1;
d1.push_back(1);
d1.push_back(2);
d1.push_back(3);
d1.push_front(5);
d1.push_front(6);
d1.push_front(7);
printDequeInt(d1);//765123
cout<<"d1的长度:"<<d1.size()<<endl;
//尾删
d1.pop_back();
//头删
d1.pop_front();
printDequeInt(d1);//6512
//插入4
d1.insert(d1.begin()+2,4);
printDequeInt(d1);//65412
//插入2个3
d1.insert(d1.begin()+3,2,3);
printDequeInt(d1);//6543312
}
int main()
{
deque01();
return 0;
}
5、stack容器:栈(先进后出,栈不提供迭代器遍历)
#include <iostream>
#include <stack>
using namespace std;
void testStack(){
stack<int> s1;
s1.push(1);
s1.push(2);
s1.push(3);
//输出一下栈内容,注意栈无法使用迭代器遍历
if(!s1.empty()){
cout<<"栈的大小为:"<<s1.size()<<endl;
while(!s1.empty()){
//top函数:返回栈顶元素
cout<<s1.top()<<" ";
//pop函数,移除栈顶元素
s1.pop();
}
cout<<endl;
}
}
int main()
{
testStack();
return 0;
}
6、queue容器(先进先出,不提供迭代器遍历)
1、queue是先进先出的数据结构,queue允许从一端增加,从一端移除
#include <iostream>
#include <queue>
using namespace std;
void testqueue(){
queue<int> q1;
q1.push(1);
q1.push(2);
q1.push(3);
//返回最后进去元素
cout<<"最后进去的元素为:"<<q1.back()<<endl;
//输出一下栈内容,注意栈无法使用迭代器遍历
if(!q1.empty()){
cout<<"q1的大小为:"<<q1.size()<<endl;
while(!q1.empty()){
cout<<q1.front()<<" ";
//pop函数,移除队头元素
q1.pop();
}
cout<<endl;
}
}
int main()
{
testqueue();
return 0;
}
7、list链表容器
1、list:采用动态存储分配,不会造成内存浪费和溢出,执行插入和删除很方便,修改指针即可
#include <iostream>
#include <list>
#include <algorithm>
using namespace std;
void printListInt(list<int> &l){
for (int e:l) {
cout<<e<<" ";
}
cout<<endl;
}
void testlist(){
list<int> l1;
l1.push_back(10);
l1.push_back(20);
l1.push_front(30);
l1.push_front(40);
printListInt(l1);//40,30,10,20
//list容器的迭代器是双向迭代器,不支持+2(随机访问迭代器支持),但支持 ++
list<int>::iterator it=l1.begin();
//it+2, 不支持
it++;
it++;
l1.insert(it,3,100);
printListInt(l1);//40,30,100,100,100,20
// STL提供的算法,只支持随机访问迭代器,而list是双向迭代器,所以STL的sort不支持list
//sort(l1.begin(),l1.back());//不支持
l1.sort();
printListInt(l1);//10,20,30,40,100,100,100
l1.reverse();//反转链表,颠倒顺序
printListInt(l1);//100,100,100,40,30,20,10
}
int main()
{
testlist();
return 0;
}
8、set/multidset容器
1、set的特性所有元素都会根据元素的键值自动被排序,set元素键值也是实值;set不允许两个元素有相同的键值(不允许重复),multiset是允许重复的,其他两者无其他差别。
2、set以及对组
#include <iostream>
#include <set>
using namespace std;
//第一部分
void printSetInt(set<int> &s){
//这里用const_iterator只读迭代器,防止修改元素,导致不是有序的容器了
set<int> :: const_iterator it=s.begin();
for (;it!=s.end();it++) {
cout<<(*it)<<" ";
}
cout<<endl;
}
void testSet(){
set<int> s1;
multiset<int> m1;
s1.insert(10);
s1.insert(20);
s1.insert(40);
s1.insert(30);
printSetInt(s1);//10,20,30,40,自动会排序
s1.insert(20);
printSetInt(s1);//还是10,20,30,40,不会重复出现
//输出元素键值为20的个数
cout<<s1.count(20)<<endl;//1
set<int>::const_iterator ret;
ret=s1.lower_bound(20);
if(ret!=s1.end()){
cout<<"下线为:"<<*ret<<endl;
}
set<int>::const_iterator ret2;
ret2 =s1.upper_bound(40);
if(ret2!=s1.end()){
cout<<"上线为:"<<*ret2<<endl;
}
//以对组的方式存储上下限
pair <set<int>::const_iterator,set<int>::const_iterator> pa;
pa=s1.equal_range(40);
if(pa.first!=s1.end()){
cout<<"下线为:"<<*(pa.first)<<endl;
}
if(pa.second!=s1.end()){
cout<<"上线为:"<<*(pa.second)<<endl;
}
//对组
pair<int,string> p1=make_pair(007,"权总");
cout<<p1.first<<" "<<p1.second;
}
3、改变排序规则,再插入前通过设置;类型时修改
//第二部分
//改变排序规则,再插入前通过设置;类型时修改
class mygreater{
public:
bool operator()(int a,int b){
return a>b;
}
};
void printSetInt(set<int,mygreater> &s){
//这里用const_iterator只读迭代器,防止修改元素,导致不是有序的容器了
set<int,mygreater> :: const_iterator it=s.begin();
for (;it!=s.end();it++) {
cout<<(*it)<<" ";
}
cout<<endl;
}
void testSet02(){
//set<int,排序规则> s2;
set<int,mygreater> s2;
s2.insert(10);
s2.insert(20);
s2.insert(40);
s2.insert(30);
printSetInt(s2);//40,30,20,10
}
4、set存放自定义数据必须修改排序规则
//第三部分
//set存放自定义数据必须修改排序规则
class myPerson;
class Person{
friend class myPerson;
friend void printSetPerson(set<Person,myPerson> &s);
private:
int num;
string name;
public:
Person(){}
Person(int num,string name){
this->num=num;
this->name=name;
}
};
class myPerson{
public:
bool operator()(Person p1,Person p2){
return p1.num<p2.num;
}
};
void printSetPerson(set<Person,myPerson> &s){
set<Person,myPerson> :: const_iterator it=s.begin();
for (;it!=s.end();it++) {
cout<<(*it).num<<" "<<(*it).name<<endl;
}
// for(Person p:s){
// cout<<p.num<<" "<<p.name<<endl;
// }
cout<<endl;
}
void testPerson(){
set<Person,myPerson> s3;
s3.insert(Person(001,"权总"));
s3.insert(Person(003,"轩总"));
s3.insert(Person(002,"煤总"));
printSetPerson(s3);
}
int main()
{
testPerson();
return 0;
}
9、map/multimap容器(红黑树)
1、map元素会根据键值自动排序,map所有元素都是pair,同时拥有键值(第一个元素),和实值(第二个元素),map不允许有重复的键值,multimap允许
#include <iostream>
#include <map>
using namespace std;
class Person{
friend void printAll(map<int,Person> m);
private:
int num;
string name;
public:
Person(){}
Person(int num,string name){
this->num=num;
this->name=name;
}
};
void printAll(map<int,Person> m){
map<int,Person>::const_iterator it=m.begin();
for (;it!=m.end();it++) {
cout<<(*it).first<<" "<<(*it).second.num<<" "<<(*it).second.name<<endl;
}
}
void testmap(){
map<int,Person> m1;
//方式一
m1.insert(pair<int,Person>(001,Person(001,"轩总")));
//方式二(推荐)
m1.insert(make_pair(002,Person(002,"权总")));
m1.insert(make_pair(003,Person(003,"煤总")));
m1.insert(make_pair(004,Person(004,"超哥")));
printAll(m1);
}
int main()
{
testmap();
return 0;
}