C++知识点总结

cpp知识点总结

一、程序结构(C-> cpp)

1、输入输出

①C
#include<stdio.h>
#include<stdlib.h>

int main()
{
    int n;
    printf("input a number:");
    scanf("%d",&n);
    printf("square:%d\n",n*n);
    return 0;
}
② cpp
#include <iostream>
using namespace std;
int main()
{
    int n;
    cout<<"input a number:";
    cin>>n;
    cout<<"square"<<n*n<<endl;
    return 0;
}

2、循环结构

两种循环语句

①for循环
#include <iostream>
using namespace std;

int main()
{
	int sum=0;
	int i;
	for(i=1;i<=100;++i)
	{
		sum=sum+i;
	}
	cout<<"sum="<<sum<<endl;
	return 0;
}
②while循环
#include <iostream>
using namespace std;

int main()
{
	int sum=0;
	int i=1;
	while(true)
	{
		sum=sum+i;
		++i;
		if(i>100)
			break;
	}
	cout<<"sum="<<sum<<endl;
	return 0;
}

do-while不推荐使用

3、分支结构

两种分支语句

①if
#include <iostream>
using namespace std;

int main()
{
	int a;
	cout<<"input a number:";cin>>a;
	if(a>0)
		cout<<"大于0"<<endl;
	else if(a<0)
		cout<<"小于0"<<endl;
	else if(a==0)
		cout<<"等于0"<<endl;
	else
		cout<<"输入错误!"<<endl;
	return 0;
}
②switch
#include <iostream>
using namespace std;

int main()
{
//	int c;
	char c;		
	cout<<"input select(1-3):";cin>>c;
	switch(c)
	{
	case '1':
		cout<<"张三"<<endl;
		break;
	case '2':
		cout<<"李四"<<endl;
		break;
	case '3':
		cout<<"王五"<<endl;
		break;
	default:
		cout<<"input error!"<<endl;
		break;
	}
	return 0;
}

4、菜单结构

#include <iostream>
using namespace std;

int main()
{
	char c;
	cout<<"=============menu==========="<<endl;
	cout<<"1. Good morning!"<<endl;
	cout<<"2. Good afternoon!"<<endl;
	cout<<"3. Exit!"<<endl;
	cout<<"============================"<<endl;
	cout<<"please select(1,2,3):";
	cin>>c;
	switch(c)
	{
	case '1':
		cout<<"Good morning!"<<endl;
		break;
	case '2':
		cout<<"Good afternoon!"<<endl;
		break;
	case '3':
		exit(0);
		break;
	default:
		cout<<"input error!"<<endl;
		break;
	}
	return 0;
}

5、主程序结构

#include <iostream>
using namespace std;

int main()
{
	char c;
	while(true)
	{
		cout<<"=============menu==========="<<endl;
		cout<<"1. Good morning!"<<endl;
		cout<<"2. Good afternoon!"<<endl;
		cout<<"3. Bye!"<<endl;
		cout<<"0. exit!"<<endl;
		cout<<"============================"<<endl;
		cout<<"please select(1,2,3,0):";
		cin>>c;
		switch(c)
		{
		case '1':
			cout<<"Good morning!"<<endl;
			break;
		case '2':
			cout<<"Good afternoon!"<<endl;
			break;
		case '3':
			cout<<"Bye!"<<endl;
			break;
		case '0':
			exit(0);
			break;
		default:
			cout<<"input error!"<<endl;
			break;
		}
	}
	return 0;
}

实例1:随机函数

用拼音首字母代表汉字,随机生成一个长度为1-5个字母的字符串,代表一部电影的片名

//随机生成电影片名 
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

int main()
{
	int n;//length
	char c;//alpha
	srand(time(0));
	n=rand()%5+1;
	while(true)
	{
		c=rand()%26+65;
		cout<<c;
		--n;
		if(n==0) break;
	}
	return 0;
} 

二、面向对象编程

1、类的使用方法

//两个数的加减法
#include <iostream>
using namespace std;

/*面向过程编程
struct cal
{
	int add(int a,int b)
	{	
		return a+b;
	}
	int sub(int a,int b)
	{
		return a-b;
	}
};*/


//面向对象编程
//struct cal// cpp叫做类

class cal
{
private:
	int a,b;
public:
	int add()
	{	
		return a+b;
	}
	int sub()
	{
		return a-b;
	}
};

int main()
{
	int a,b;
//	cout<<"input a:";	cin>>a;
//	cout<<"input b:";	cin>>b;
	cal c;	//对象c
	c.a=a;
	c.b=b;
	cout<<c.add()<<endl;
	cout<<c.sub()<<endl;
	return 0;
}

2、类成员的属性、初始化——构造函数

#include <iostream>
using namespace std;

class cal
{
private:
	int a,b;
public:	
	cal(int aa,int bb)
	{
		a=aa;b=bb;
	}
	void input()
	{
		cout<<"input a:";cin>>a;
		cout<<"input b:";cin>>b;
	}
	int add()
	{
		return a+b;
	}
	int sub()
	{
		return a-b;
	}
	void output()
	{
		cout<<a<<"+"<<b<<"="<<add()<<endl;
		cout<<a<<"-"<<b<<"="<<sub()<<endl;
	}
};

int main()
{
	cal c(10,20);
//	c.input();
	c.output();
	return 0;
}

实例1:平面点类

(1)数据:点的坐标
(2)函数:输入、输出

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

class test
{
private:
	int x,y;
public:
	test(int xx,int yy)
	{
		x=xx;
		y=yy;
	}
	void input()
	{
		cout<<"input x:";cin>>x;
		cout<<"input y:";cin>>y;
	}
	void output()
	{
		cout<<"point:("<<x<<","<<y<<")"<<endl;
	}
};

int main()
{
	test t1(10,20);
	test t2(30,40);
//	t.input();
	t1.output();
	t2.output();
	return 0;
}

实例2:简单书籍类

(1)数据:数量、书名、价格
(2)函数:输入、输出

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

class test
{
private:
	string name;
	double price;
	int number;
public:
	test(string ss,double dd,int ii)//鏋勯€犲嚱鏁?
	{
		name=ss;price=dd;number=ii;
	}
	void input()
	{
		cout<<"input name:";cin>>name;
		cout<<"input price:";cin>>price;
		cout<<"input number:";cin>>number;				
	}
	void output()
	{
		cout<<"name:"<<name<<endl;
		cout<<"price:"<<price<<endl;
		cout<<"number:"<<number<<endl;	
	}
	double money()
	{
		return price*number;
	}
};

int main()
{
	test t("鐢熷懡鐨勭瀵?,34.6,50);
	t.output();
	cout<<t.money()<<endl;	
	return 0;
}

实例3:抽奖程序(随机数)

(1)从n名同学中抽取1名同学?
(2)从n名同学中不重复抽取m名同学?
(3)实际的抽奖过程:中奖者不参加后续抽奖(使用容器)

#include <iostream>
#include <ctime>
using namespace std;
//30个人

class test
{
private:
	int n,m;
public:
	test(int nn,int mm)//构造函数
	{
		n=nn;m=mm;
		srand(time(0)/*200*/);//随机数随着时间变化	//永远也得不了奖
	}
	void output()
	{
		int i,temp;
		int* a=new int[n];//动态数组分配空间
		for(i=0;i<n;++i)
		{
			a[i]=0;
		}
		//int a[n]={0};
		//for(i=0;i<n;)//笨拙方法
		for(i=0;i<m;++i)
		{
			while(true)
			{
				temp=rand()%n;
				if(a[temp]==0)
				{
					cout<<temp+1<<endl;
					a[temp]=1;
					break;
				}
			}
		}
		delete [] a;
	}
};

int main()
{
	test t(30,10);
	t.output();
	return 0;
}	

⚠ 补充:动态分配内存

1、单一变量
int* a;
a = new int;
...
delete a;
2、数组
int* a;
a = new int[n];
...
delete [] ar;

⚠ 实例4:输入密码的程序(无回显输入)

已设定好密码,输入密码,正确显示“Yes!”,错误提示“Wrong ,try again:”,三次错误显示“You dead!”。

1、基本实现
(1)无回显输入:getch() //#include <conio.h>
(2)键盘码识别(含控制键)
2、基本功能
3、加入“退格”键盘

//输入密码的程序:考虑退格键
#include <iostream>
#include <string>
#include <conio.h>
using namespace std;

class password
{
private:
	string pass1,pass2;
	int times;
	char c;
	int i;
public:
	password(string s,int n)
	{
		pass1=s;
		times=n;
	}
	void input()
	{
		cout<<"input password:";
		while(true)
		{
			c=getch();
			if(c==13)//检测回车键
			{
				if(pass1==pass2)
				{
					cout<<endl<<"right!"<<endl;
					break;
				}
				else
				{
					--times;//减少一次机会
					if(times>0)
					{
						cout<<endl<<"wrong!"<<endl;
						cout<<"lase "<<times<<" times"<<endl;
						cout<<"press anykey continue..."<<endl;
						getch();
						system("cls");//清屏
						cout<<"input password:";
						pass2="";
					}
					else
					{
						cout<<endl<<"you dead!"<<endl;
						break;	
					} 
				}
			}
			else if(c==8)//检测退格(backspace)
			{
				if(pass2.size()>0)
				{
					system("cls");
					cout<<"input password:";
					pass2.erase(pass2.size()-1,1);
					for(i=0;i<pass2.size();++i)
					{
						cout<<"*";
					}
				}
			}
			else//正常输入替换无回显
			{
				pass2=pass2+c;
				cout<<"*";
			}
		}
	}
	string value()
	{
		return pass2;
	}
};

int main()
{
	password p1("123456",3);
	p1.input();
//	cout<<endl<<p1.value()<<endl;
	return 0;
}

三、类的使用-初始化

复习:面向对象编程(类)

1、简单类:求一个整数的平方
#include <iostream>
using namespace std;

class test
{
private:
	int n;
public:
	test(int nn)
	{
		n=nn;
	}
	void input ()
	{
		cout<<"input n:";cin>>n;
	}
	int output()
	{
		return n*n;
	}
};//分号不要忘写

int main()
{
	test t(10);
	//test t;
//	t.input();
	cout<<t.output()<<endl;
	return 0;
}
2、输入密码的程序:getch()//#include <conio.h>

见上二-实例4

⚠ 3、模拟屏幕保护程序:kbhit()//#include <conio.h>
#include <iostream> 
#include <conio.h>
using namespace std;

class screen
{
private:
	int n;
public:
	screen()
	{
		n=0;
	}	
	void move()
	{
		while(true)
		{
			cout<<n;
			++n;
			if(n>10000) 
                n=0;
			if(kbhit())//检测是否有键盘输入
                break;
		}
	}
};

int main()
{
	screen s;
	s.move();
	return 0;
}

⚠ 1、函数参数的缺省值

#include <iostream>
using namespace std;

int f(int n=10)
{
    return n*n;
}
int main()
{
    cout<<f(12)<<endl;
    cout<<f()<<endl;
	return 0;
}

⚠ 2、函数的重载

#include <iostream>
using namespace std;

int square(int n=10)
{
	return n*n;
}

double square(double n)
{
	return n*n;
}

int main()
{
	int a=10;
	double b=2.5;
	cout<<square(a)<<endl;
	cout<<square(b)<<endl;
}
#include <iostream>
using namespace std;

//函数的重载
//识别括号里的数据类型、个数
//参数的类型个数不一样时候适用
int add(int n)
{
	return n*n;
}
int add(int x,int y,int z)
{
	return x+y+z;
}

int main()
{
	cout<<add(1,2)<<endl;
	return 0;
}

3、构造函数的重载

#include <iostream>
using namespace std;

class Square
{
private:
	int n;
public:
	Square()
	{
		n=10;
	} 
	Square(int nn)//nn是为了跟第一个构造函数区别开
	{
		n=nn;
	}
//	Square(int nn=10){n=nn;}
	void input()
	{
		cout<<"input n:";
		cin>>n;
	}
	void output()
	{
		cout<<"square:"<<n*n<<endl;
	}
};

int main()
{
	Square s1,s2(20);
	s1.output();
	s2.output();
	return 0;
}

练习

1、从10个整数中找出一个最大的数并且输出?
//从10个整数中找出一个最大的数并输出 
#include <iostream>
using namespace std;

class test
{
private:
	int a[10];
public: 
	test()
	{
		int i;
		for(i=0;i<10;++i)
		{
			a[i]=i+1;
		}
	}
	void input()
	{
		int i;
		for(i=0;i<10;++i)
		{
			cin>>a[i];
		}
	}
	int max()
	{
		int max=a[0],i;
		for(i=0;i<10;++i)
		{
			if(max<a[i])
				max=a[i];
		}
		return max;
	}
};

int main()
{
	test t;
	t.input();
	cout<<t.max()<<endl;
	return 0;
}
//-----------------------------
#include <iostream>
using namespace std;

class test
{
private:
    int a[10];
public:
    test()
    {
        int i;
        for(i=1;i<=10;++i)
        {
        	a[i-1]=i%10;
		}
    }
    test(int n)//n是为了跟第一个构造函数区别开
    {
        //输入数据
        int i;
        for(i=0;i<10;++i)
        {
        	cin>>a[i];
        }
    }
    int output()
    {
        int i,max=a[0];
        for(i=0;i<10;++i)
        {
        	if(max<a[i])
        		max=a[i];
		}
        return max;
    }
};

int main()
{
    test t;
    cout<<t.output()<<endl;
    return 0;
}
2、求整数n的阶乘?(思考:递归程序怎么编?)
//求整数n的阶乘法 
#include <iostream>
using namespace std;

class test
{
private:
	int n;
public: 
	test()
	{
		n=5;
	}
	test(int nn)
	{
		n=nn;
	}
	int output()
	{
		if(n==0) return 1;
		int i,m=1;
		for(i=1;i<=n;++i)
		{
			m=m*i;
		}
		return m;
	}
};

int main()
{
	test t(6);
	cout<<t.output()<<endl;
	return 0;
}
3、求两个整数之差的绝对值?(自定义或使用库函数)
//求两个整数之差的绝对值 
#include <iostream>
using namespace std;

class test
{
private:
	int a,b;
public: 
	test(){a=10;b=20;}
	test(int aa,int bb){a=aa;b=bb;}
	void input()
	{
		cout<<"input first number:";cin>>a;
		cout<<"input second number:";cin>>b;
	}
	void output()
	{
		if(a-b>=0)
			cout<<a-b<<endl;
		else
			cout<<b-a<<endl;
	}
};

int main()
{
	test t(6,12);
	t.output();
	return 0;
}
4、输入2021年某个月份,返回该月的天数?(闰年如何判断?)
//输入月份,返回天数 
#include <iostream>
using namespace std;

class test
{
private:
	int m;
public: 
	test()
	{
		m=1;
	}
	test(int mm)
	{
		m=mm;
	}
	void input()
	{
		cout<<"input month:";
		cin>>m;
	}
	int output()
	{
		if(m==2)
			return 28;
		else if(m==4||m==6||m==9||m==11)
			return 30;
		else
			return 31;
	}
};

int main()
{
	test t;
	t.input();
	cout<<t.output()<<endl;
	return 0;
}
5、输出小于整数n的所有素数(质数)?
#include <iostream>
#include <math.h>
using namespace std;

class test
{
private:
	int n;
public:
	test()
	{
		n=10;
	}
	test(int nn)
	{
		n=nn;
	}
	int output()
	{
		int i;
		for(i=2;i<=n;++i)
		{
			if(is_prime(i))
				cout<<i<<endl;
		}
		return 0;
	}
	int is_prime(int x)//判断是不是素数
	{
		int m,i,flag=1;
		m=x/2;
		for(i=2;i<=m;++i)
		{
			if(x%m==0)
				flag=0;
		}
		return flag;
	}
};

int main()
{
	test t(1000);
	t.output();
	return 0;
}

四、指针与引用

复习:函数重载

#include <iostream>
#include <string>
using namespace std;
int add(int a,int b)
{
	return a+b;
}
double add(double a,double b)
{
	return a+b;
}
string add(string a,string b)
{
	return a+b;
}

int main()
{
	cout<<add(1,2)<<endl;
	cout<<add(1.5,2.3)<<endl;
	cout<<add("hello","world")<<endl;
}

1、指针的定义

#include <iostream>
using namespace std;

int main()
{
	int a=10,b=20;
	int* p;
	p=&a;
	cout<<*p<<endl;
	cout<<p<<endl;//指针p的值(变量a的地址) 
	p=&b; 
	cout<<*p<<endl; 
	cout<<p<<endl;//指针p的值(变量b的地址)
	return 0;
}

2、指针的使用

(1)静态

交换两个数

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

void swap(int* x,int* y)
{
    int temp;
    temp=*x;
    *x=*y;
    *y=temp;
}

int main()
{
    int a=10,b=20;
    swap(&a,&b);
    cout<<a<<endl;
    cout<<b<<endl;
    system("pause");
	return 0;
}

system(“pause”)的用途:
让程序暂停一下
求整数平方的程序

⚠(2)动态申请内存
①C变量
#include <iostream>
#include <cstdlib>
using namespace std;

int main()
{
	int* p;
	p=(int*)malloc(sizeof(int)); 
	scanf("%d",p);
	printf("%d\n",*p);
	free(p);
	return 0;
} 
②C数组
#include <iostream>
#include <cstdlib>//要用这个 
using namespace std;

int main()
{
	int a[3];
	printf("%x	%x	%x	%x",a,a[0],&a,&a[0]);
	int* p;
	int n=3,i;
	//动态分配
	p=(int*)malloc(sizeof(int)*n);
	for(i=0;i<3;++i)
	{
		scanf("%d",&p[i]);
	}
	for(i=0;i<3;++i)
	{
		printf("%d\n",p[i]);
	}
	free(p);
//	scanf("%d",p);
//	printf("%d\n",*p);
	return 0;
}

③ cpp变量
#include <iostream>
using namespace std;

int main()
{
	int* p;//
	p=new int;
	cin>>*p;
	cout<<*p<<endl;
	delete(p);//
	return 0;
} 
④ cpp数组
#include <iostream>
using namespace std;

int main()
{
	int* p;
	int n=3,i; 
	p=new int[n];//
	for(i=0;i<n;++i)
	{
		cin>>p[i];
	} 
	for(i=0;i<n;++i)
	{
		cout<<p[i]<<endl;
	} 
	delete [] p;//
	return 0;x
}
⑤ cpp结构体
#include <iostream>
#include <cstdlib>
#include <string>
using namespace std;

struct student
{
	string name;
	int number;
	double score;
};

int main()
{
	student* p;
	p=new student;//
	cin>>(*p).number;//
	cin>>p->score;//
	cout<<(*p).number<<endl;
	cout<<p->score<<endl;
	delete p;
	return 0;
 } 
⑤ cpp结构体数组
#include <iostream>
#include <cstdlib>
#include <string>
using namespace std;

struct student
{
	string name;
	int number;
	double score;
};

int main()
{
	int* p;
	int n=3,i;
	p=new int[n];
	for(i=0;i<n;i++)
	{
		cin>>p[i];
	}
	for(i=0;i<n;i++)
	{
		cout<<p[i]<<endl;
	}
	delete [] p;//释放掉p和指向的数组部分 
	return 0;
 } 

3、测试动态数组

#include <iostream>
#include <cstdlib>
using namespace std;

int f(int m[],int n)
{
	int i,sum=0;
	for(i=0;i<n;++i)
	{
		sum=sum+i;
	}
	return sum;
}

int main()
{
	int/**/* a/**/,b[5],i;
	a=new int[5];
	cout<<"a="<<a<<endl;
	cout<<"&a="<<&a<<endl;
	cout<<"&a[0]="<<&a[0]<<endl;
	cout<<"&a[1]="<<&a[1]<<endl;
	cout<<"=============="<<endl;
	cout<<"b="<<b<<endl;
	cout<<"&b="<<&b<<endl;
	cout<<"&b[0]="<<&b[0]<<endl;
	cout<<"&b[1]="<<&b[1]<<endl;
	for(i=0;i<5;++i)
	{
		a[i]=i;
		b[i]=i;
	}
	cout<<"=============="<<endl;
	cout<<f(a,5)<<endl;
	cout<<f(b,5)<<endl;
	system("pause");
	return 0;
}

4、引用的定义与使用

#include <iostream>
#include <cstdlib>
#include <string>
using namespace std;

int main()
{
	int a=100,b=200;
	int& p=a;//p叫做变量a的引用 //就是把a改了个名字
	p=b;
	cout<<a<<endl;
	return 0;
}

引用与指针的区别
(1)指针是一个变量,有自己的值
指向可变,运行时有效。
(2)引用不是一个变量,
指向不可变,编译时有效

5、引用的用途:作函数参数

实例:swap函数:交换两个变量的值

1、不用指针,无法交换
2、使用指针,可以交换,但程序可读性较差
3、使用引用,可以交换,只在形参处变化
4、问题:指针做参数时,实参不取地址也有效,
不知道为什么?见右图。

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

void swap1(int& a,int& b)//引用的作用:参数的传入,简化写法 ——增加程序可读性,节省内存空间 
{
	int temp=a;
	a=b;
	b=temp;
}
// cpp中可以用引用代替指针 

int main()
{
	int x=100,y=200;
	cout<<"x="<<x<<endl;
	cout<<"y="<<y<<endl;
	cout<<"=============swap1()================"<<endl;
	swap1(x,y);//swap是库函数
	cout<<"x="<<x<<endl;
	cout<<"y="<<y<<endl;
	return 0;
}

练习

1、评委打分程序

某项体育赛事共有n个评委打分,满分为10分,不允许打负分。给某个选手打分后,除去一个最高分和一个最低分,剩余评委的分数相加为该选手的本轮得分。
用类实现该程序,构造函数规定评委人数n。input函数输入n个评委的打分,output函数输出某选手的本轮得分。

①动态数组
#include <iostream>
#include <cstdlib>
#include <string>
using namespace std;

class test
{
private: 
	int n;
	int* a;
public:
	test(int nn)
	{
		n=nn;
//		a=new int[n];//构造函数定义的数组必须在析构函数中释放掉 
	}
	void input()
	{
		int i;
		for(i=0;i<n;++i)
		{
			cin>>a[i];
		}
	}
	int output()
	{
		int i,sum=0,max=a[0],min=a[0];
		for(i=0;i<n;++i)
		{
			sum=sum+a[i];
			if(max<a[i])
				max=a[i];
			if(min>a[i])
				min=a[i];
		}
		sum=sum-max-min;
		delete [] a;
		return sum;
	}
//	~test()//析构函数——对象注销的时候运行 
//	{
//		delete [] a;
//	}
};

int main()
{
	test t(6);
	t.input();
	cout<<t.output()<<endl;
	return 0;
}
②容器
#include <iostream>
#include <cstdlib>
#include <vector>
using namespace std;

class Select
{
private:
	vector<int> a;
	int n;
public:
	Select(int nn)
	{
		n=nn;
	}
	void input()
	{
		int i,temp;
		for(i=0;i<n;++i)
		{
			cout<<"input score:";cin>>temp;
			a.push_back(temp);
		}
	}
	void output()
	{
		int i;
		int max=0,min=11,sum=0;
		for(i=0;i<n;++i)
		{
			sum=sum+a[i];
			if(a[i]>max)
			{
				max=a[i];
			}
			if(a[i]<min)
			{
				min=a[i];
			}
		}
		sum=sum-min-max;
		cout<<"score="<<sum<<endl;
	}
};

int main()
{	
	Select s(6);
	s.input();
	s.output();
	system("pause");
	return 0;
}
⚠③链表
#include <iostream>
#include <cstdlib>
using namespace std;

struct score
{
	int number;
	score* next;
};

class Select
{
private:
	int n;
	score* head,* end,* p;
public:
	Select(int nn)
	{
		n=nn;
	}
	void input()
	{
		head=new score;
		end=head;
		int i;
		for(i=0;i<n;++i)
		{
			p=new score;
			cout<<"input score:";cin>>p->number;
			end->next=p;
			end=p;
		}
		end->next=0;
	}
	void browse()
	{
		p=head;
		while(true)
		{
			p=p->next;
			if(p==0)break;
			cout<<p->number<<endl;
		}
	}
	void output()
	{
		int i;
		int max=0,min=11,sum=0;
		p=head;
		while(p->next!=0)
		{
			p=p->next;
			sum=sum+p->number;
			if(p->number>max)
			{
				max=p->number;
			}
			if(p->number<min)
			{
				min=p->number;
			}
		}
		sum=sum-min-max;
		cout<<"score="<<sum<<endl;
	}
};

int main()
{	
	Select s(3);
	s.input();
	s.browse();
//	s.output();
	return 0;
}
2、简单类:给不及格的同学提分

函数要求:
(1)构造函数:确定n的数值
(2)数据输入函数:输入n名同学的分数(整数);
(3)提分函数:60分的提到61分;不及格的同学提到60分;
(4)输出函数:输出n名同学的最后得分

①动态数组
#include<iostream>
#include<cmath>
#include<ctime>
#include<string>
using namespace std;

struct S
{
    string number;
    double score;
};
class test
{
private:
    int n;
    struct S* a = new S[n];
public:
    test(int nn)
    {
        n = nn;
        srand(time(0));
    }
    void input()
    {
        int i;
        for (i = 0; i < n; ++i)
        {
            cin >> a[i].number;
            cin >> a[i].score;
        }
    }
    void prize()
    {
        int i;
        i = rand() % n;
        cout << a[i].number << endl;
        cout << a[i].score << endl;
    }
    ~test()
    {
    	delete [] a;
	}
};
int main()
{
    test a(3);
    a.input();
    a.prize();
}
②容器
#include <iostream>
#include <cstdlib>
#include <vector>
using namespace std;

class test
{
private:
	int n;
	vector<int> a;
public:
	test(int nn)
	{
		n=nn;
	}
	void input()
	{
		int i,temp; 
		for(i=0;i<n;++i)
		{
			cout<<"input "<<i+1<<" :";
			cin>>temp;
			a.push_back(temp);
		}
	}
	void output()
	{
		int i; 
		cout<<"----------------"<<endl;
		for(i=0;i<n;++i)
		{
			cout<<"output "<<i+1<<" :"<<a[i]<<endl;
		}
	}
	void add()
	{
		int i; 
		for(i=0;i<n;++i)
		{
			if(a[i]==60)
				a[i]=61;
			else if(a[i]<60)
				a[i]=60;
		}
	}
};

int main()
{
	test t(10);
	t.input();
	t.add();
	t.output();
	system("pause");
	return 0;
}

五、类的继承与派生

复习:

(1)指针(test1)
#include <iostream>
#include <cstdlib>
#include <string>
using namespace std;

int main()
{
	int* p;
	cout<<"addr of p is:"<<&p<<endl;
	p=new int;
	*p=100;
	cout<<"addr of p-> is:"<<p<<endl;
	cout<<"addr of *p is:"<<&(*p)<<endl;
	delete p;
	return 0;
}
// cpp使用容器替代指针
(2)引用(test2)
#include <iostream>
#include <cstdlib>
#include <string>
using namespace std;

char change(char& c)
{
	if(c>='a'&&c<='z')
		return c-32;
	else if(c>='A'&&c<='Z')
		return c+32;
}

int main()
{
	char c;
	cout<<"input a char:";cin>>c;
	cout<<"the char is:"<<change(c)<<endl;
	return 0;
}

⚠(一)继承与派生

意义:符合自然规律:事物之间的联系(生物进化)

image-20210404133709435
#include <iostream>
#include <cstdlib>
using namespace std;
//继承与派生

class A
{
protected://protected家族——受保护
	int m;
public:
	A()
	{
		m=1000;
	}
	A(int mm)
	{
		m=mm;
	}
	void input()
	{
		cout<<"input m:";cin>>m;
	}
	void output()
	{
		cout<<"m="<<m<<endl;
	}
};//注意‘;’

class B:public A//公有继承//软件重用
{
protected:
	int n;
public:
    //派生类的构造函数
	B():A()//子类构造函数数量不能少于父类//父类重载子类也必须重载
	{
		n=2000;
	}
	B(int mm,int nn):A(mm)//加了A(mm)上面A也得加一个构造函数
	{
		n=nn;
	}
	void input()
	{
		A::input();//重载之后也能用父类的函数
		cout<<"input n:";cin>>n;
	}
	void output()
	{
		A::output();//重载之后也能用父类的函数
		cout<<"n="<<n<<endl;
	}
};//注意‘;’

int main()
{
	//B b(10,20);
	B b;
//	b.input();//都有input output——重载,用自己的函数
	b.output();//上面加入A::input();重载之后也能用父类的函数
	return 0;
}

实例:

1、平面点类→空间点类
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <string>
#include <conio.h>
using namespace std;
const double PI=3.1415926;//设置常量

class point
{
protected:
	int x,y;
public:
	point()
	{
		x=0;y=0;
	}
	point(int xx,int yy)
	{
		x=xx;y=yy;
	}
	void output()
	{
		cout<<"("<<x<<","<<y<<")"<<endl;
	}
};

class circle:public point
{
protected:
	int r;
public:
	circle()
	{
		r=10;
	}
	circle(int xx,int yy,int rr):point(xx,yy)
	{
		r=rr;
	}
	void output()
	{
		point::output();
		cout<<"r="<<r<<endl;
		cout<<area()<<endl;
	}
	double area()
	{
		return r*r*PI;
	}
};

int main()
{
	circle c1(1,2,12),c2(3,4,8);
	if(c1.area()>c2.area())
		c1.output();
	else
		c2.output();
//	c.output();
	return 0;
}
2、日期类:年类、月类、日类三层派生
#include <iostream>
#include <cstdlib>
using namespace std;

class Y
{
protected:
	int y;
public:
	Y(){y=2021;}
	Y(int yy){y=yy;}
};//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

class M:public Y
{
protected:
	int m;
public:
	M(){m=3;}
	M(int yy,int mm):Y(yy){m=mm;}
	M(int mm)
	{
		m=mm;
	}
};

class D:public M//写父亲不要写爷爷!
{
protected:
	int d;
public:
	D()
	{
		d=23;
	}
	D(int yy,int mm,int dd):M(yy,mm)//只管父亲
	{
		d=dd;
	}
	D(int dd)
	{
		d=dd;
	}
	D(int mm,int dd):M(mm)//父类必须得追加一个只有mm的构造函数
	{
		d=dd;
	}
	void output()
	{
		cout<<y<<"-"<<m<<"-"<<d<<endl;
	}
};

int main()
{
//	D d;
//	D d(2022,3,23);
//	D d(24);
	D d(4,3);
	d.output();
	return 0;
}

练习

1、继承与派生:从点类派生出圆类
#include <iostream>
#include <cstdlib>
using namespace std;
const double PI=3.1415926;//常量 

class Point
{
protected:
	int x,y;
public:
	Point(int xx,int yy)
	{
		x=xx;y=yy;
	}
	void output()
	{
		cout<<"x="<<x<<endl;
		cout<<"y="<<y<<endl;
	}
};

class Circle:public Point
{
protected:
	double r;
public:
	Circle(int xx,int yy,double rr):Point(xx,yy)
	{
		r=rr;
	}
	void output()
	{
		cout<<"x="<<x<<endl;
		cout<<"y="<<y<<endl;
		cout<<"r="<<r<<endl;
	}
	void area()
	{
		cout<<"circle area="<<PI*r*r<<endl;
	}
};

int main()
{
	Circle c(1,2,3);
	c.output();
	c.area();
	return 0;
}
2、继承与派生:从商品数量类派生出商品总值类
#include <iostream>
#include <cstdlib>
using namespace std;

class Number
{
protected:
	int n;
public:
	Number()
	{
		n=10;
	}
	Number(int nn)
	{
		n=nn;
	}
	void output()
	{
		cout<<"number="<<n<<endl;
	}
};

class Price:public Number
{
protected:
	double p;
public:
	Price()
	{
		p=10;
	}
	Price(int nn,double pp):Number(nn)
	{
		p=pp;
	}
	void output()
	{
		cout<<"number="<<n<<endl;
		cout<<"price="<<n<<endl;
	}
	double tatol()
	{
		return n*p;
	}
};

int main()
{
	Price p1(10,10),p2(20,20);
	if(p1.tatol()>p2.tatol())
		p1.output();
	else
		p2.output();
	return 0;
}
3、简单类:给不及格的同学提分
#include <iostream>
#include <cstdlib>
using namespace std;

class test
{
private:
	int n;
	int* a;
public:
	test(int nn)
	{
		n=nn;
		a=new int[n];
	}
	void input()
	{
		int i;
		for(i=0;i<n;++i)
		{
			cout<<"input score:";cin>>a[i];
		}
	}
	void output()
	{
		int i;
		for(i=0;i<n;++i)
		{
			if(a[i]==60)
				a[i]=61;
			else if(a[i]<60)
				a[i]=60;
			cout<<a[i]<<endl;
		}
	}
	~test()
	{
		delete[]a;
	}
};

int main()
{
	test t(5);
	t.input();
	t.output();
	return 0;
}
4、求级数的和:a1+a2+a3+…+an+…(an=a(n-1)+m)
#include <iostream>
#include <cstdlib>
using namespace std;

class test
{
private:
	int n,m;
public:
	test(int nn,int mm)
	{
		n=nn;m=mm;
	}
	int output()
	{
		int i,temp=0,temp1=1;
		for(i=1;i<=n;++i)
		{
			temp=temp+temp1;
			temp1=temp1+m;
		}
		return temp;
	}
};

int main()
{
	test t(5,3);
	cout<<t.output()<<endl;
	return 0;
}

⚠ 六、运算符重载

1、友元函数

可以让函数中调用类里面的函数
但是不能直接使用类里面的元素,需要这么写:a.m

class A
{
protected:
	int m;
public:
	A()
	{
		m=100;
	}
	void input()
	{
		cout<<"input m:";
		cin>>m;
	}
	void output()
	{
		cout<<"m="<<m<<endl;
	}	
	friend void display(A a);//友元函数,可以让display使用类里面的函数
};

void display(A a)
{
	a.output();
	cout<<a.m<<endl;//不能使用类里面的数据
}

int main()
{
	A a;
	display(a);
	return 0;
}

2、输入/输出运算符的重载(在类外)

重载的格式
(1)输入运算符
istream& operator>>(istream& os,A& a)//因为要传回去所以A要取址&
{
	a.input();
	return os;
}
(2)输出运算符
ostream& operator<<(ostream& os,A a)
{
	a.output();
	return os;
}

放在类内:前面加friend!!!

程序实现
//输入输出运算符的重载——为了直接对类里头的对象进行计算
#include <iostream>
using namespace std;

class A
{
protected:
	int m;
public:
	A()
	{
		m=100;
	}
	void input()
	{
		cout<<"input m:";
		cin>>m;
	}
	void output()
	{
		cout<<"m="<<m<<endl;
	}	
	friend void display(A a)//友元函数,可以让display使用类里面的函数
	{
		a.output();
	}
	//这么放整齐一点——变成友元函数
	friend istream& operator>>(istream& os,A& a)//得有个引用&,得让值带回来!!!⚠
	{
		a.input();
		return os;
}	
	friend ostream& operator<<(ostream& os,A a)
	{
		a.output();
		return os;
	}	
	
};

/*
void display(A a)
{
	a.output();
	cout<<a.m<<endl;//不能使用类里面的数据
}*/

/*第一类运算符重载——输入输出运算符
istream& operator>>(istream& os,A& a)//得有个引用&,得让值带回来
{
	a.input();
	return os;
}
ostream& operator<<(ostream& os,A a)
{
	a.output();
	return os;
}*/

int main()
{
	A a;
	cin>>a;
	cout<<a;
	return 0;
}

3、双目运算符的重载(在类内)

返回值为对象的含义:数据

双目运算符重载的格式
1、+
A operator+(A a)
{
	A temp; 
	temp.a=m+a.m;
    return temp;
}
2、>
bool operator>(A a2)
{
    return m>a2.m;//☆
}
3、<
程序实现
//第二类运算符的重载——+><
#include <iostream>
using namespace std;

class A
{
protected:
	int m;
public:
	A(){m=100;}
	A(int mm){m=mm;}
	void input()
	{
		cout<<"input m:";
		cin>>m;
	}
	void output()
	{
		cout<<"m="<<m<<endl;
	}	
	friend void display(A a)//友元函数,可以让display使用类里面的函数
	{
		a.output();
	}
	//这么放整齐一点——变成友元函数
	friend istream& operator>>(istream& os,A& a/*(10)*/)//得有个引用&,得让值带回来	//小a后面不能像主函数a一样后面带参数
	{
		a.input();
		return os;
	}	
	friend ostream& operator<<(ostream& os,A a)
	{
		a.output();
		return os;
	}	
	A operator+(/*A a1,*/A a2)//双目运算符第一个数规定就是自己——写一个参数即可 
	{
		A temp;
		temp.m=m+a2.m;
		return temp;
	}
	bool operator>(A a2)
	{
		/*
		A temp;
		temp.m=m;
		if(temp.m>a2.m)
			return temp;
		else
			return a2
		*/
		return m>a2.m;//☆
	}
};

int main()
{
	A a1(50),a2(300),a3;
	//a3=a1>a2;//不好,尽量不要改变运算符原有的属性
	a3=a1+a2;
	cout<<a3;
	
	if(a1>a2)//☆
		cout<<a1;
	else
		cout<<a2;
	
	return 0;
}	

4、单目运算符的重载(++)

格式
(1)++i
A operator++()
{
	A temp; 
	temp.i=i+1; 
	i=i+1; 
	return temp;
}
(2)i++
A operator++(int w)
{
	A temp; 
	temp.i=i; 
	i=i+1; 
	return temp;
}
程序实现
#include <iostream>
using namespace std;

//第三类运算符的重载——++,--
class A
{
protected:
	int m;
public:
	A()
	{
		m=100;
	}
	A(int mm)
	{
		m=mm;
	}
	void input()
	{
		cout<<"input m:";
		cin>>m;
	}
	void output()
	{
		cout<<"m="<<m<<endl;
	}	
	friend void display(A a)//友元函数,可以让display使用类里面的函数
	{
		a.output();
	}
	//这么放整齐一点——变成友元函数
	friend istream& operator>>(istream& os,A& a/*(10)*/)//得有个引用&,得让值带回来	//小a后面不能像主函数a一样后面带参数
	{
		a.input();
		return os;
	}	
	friend ostream& operator<<(ostream& os,A a)
	{
		a.output();
		return os;
	}	
	A operator+(/*A a1,*/A a2)//双目运算符第一个数规定就是自己——写一个参数即可 
	{
		A temp;
		temp.m=m+a2.m;
		return temp;
	}
	bool operator>(A a2)
	{
		return m>a2.m;
	}
	A operator++()//++i
	{
		A temp;
		temp.m=m+1;//表达式
		m=m+1;//自身值
		return temp;
	}
	A operator++(int w/*为了与第一个区分开*/)//i++
	{
		A temp;
		temp.m=m;//表达式
		m=m+1;//自身值
		return temp;
	}
};

int main()
{
	A a(10),b;
	//b=++a;
	b=a++;
	cout<<a<<endl;
	cout<<b<<endl;
	return 0;
}
this指针
#include <iostream>
using namespace std;

//this指针
class A
{
protected:
	int m;
public:
	A()
	{
		m=100;
	}
	A(int mm)
	{
		m=mm;
	}
	void input()
	{
		cout<<"input m:";
		cin>>m;
	}
	void output()
	{
		cout<<"m="<<m<<endl;
	}	
	friend void display(A a)//友元函数,可以让display使用类里面的函数
	{
		a.output();
	}
	//这么放整齐一点——变成友元函数
	friend istream& operator>>(istream& os,A& a/*(10)*/)//!!!!!!!!!!!!!!!!!
	//得有个引用&,得让值带回来	//小a后面不能像主函数a一样后面带参数
	{
		a.input();
		return os;
}	
	friend ostream& operator<<(ostream& os,A a)
	{
		a.output();
		return os;
	}	
	A operator+(/*A a1,*/A a2)//双目运算符第一个数规定就是自己——写一个参数即可 
	{
		A temp;
		temp.m=m+a2.m;
		return temp;
	}
	bool operator>(A a2)
	{
		return m>a2.m;
	}
	A operator++()//++i
	{
		++m;
		return *this;//this指针指向某个对象,在类里面使用//this是类的地址,*this是类
				 	 //*this返回自己的类
	}
	A operator++(int w/*为了与第一个区分开*/)//i++
	{
		A old(*this);//定义对象old并用*this类赋值——记住这个写法
//也可以写成A old = *this;
		++(*this);
		return old;
	}
};

int main()
{
	A a(10),b;
	b=++a;
	//b=a++;
	cout<<a<<endl;
	cout<<b<<endl;
	return 0;
}

5、“赋值=”与“等于==”运算符的重载

//赋值与等于运算符重载 
#include <iostream>
using namespace std;

class point
{
private:
	int x,y;
public:
	point(int xx,int yy)
	{
		x=xx;y=yy;
	}
	operator==(point p)//前面加个bool也能运行
	{
		return (x==p.x && y==p.y);
	}
	operator=(point p) 
	{
		x=p.x;
		y=p.y;
	}
/*或者将上面的函数写成这样
	point& operator=(point& p)
	{
		this->x=p.x;
		this->y=p.y;
		return p;
	}
*/
/*或者将上面的函数写成这样
	operator=(point& p) 
	{
		this->x=p.x;
		this->y=p.y;
	}
*/
//下面的函数必须加“friend”,因为“<<”重载不能在类内
	friend ostream& operator<<(ostream& os,point& p)
	{
		cout<<"("<<p.x<<","<<p.y<<")"<<endl;
		return os;
	}
};

int main()
{
	point p1(1,2),p2(3,4);
	if(p1==p2)
		cout<<"yes!"<<endl;
	else
		cout<<"no!"<<endl;
	p1=p2;
	cout<<p1;
	return 0;
}

练习

1、

​ 圆类,数据为点的坐标x、y和半径r,内含构造函数(参数初始化)、数据输出函数(输出点的坐标和半径)、返回圆面积函数。
​ 在类中对输出运算符进行重载,输出圆心的坐标、半径、圆面积。
​ 在主函数定义圆类对象,使用“<<”输出四项数据。

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
const double PI=3.1415926;//!!!!!!!!!!!!!!!!!!!!!

class test
{
private:
	int x,y,r;
public:
	test(int xx,int yy ,int rr)
	{
		x=xx;y=yy;r=rr;
	}
	void output()
	{
		cout<<"x="<<x<<endl;
		cout<<"y="<<y<<endl;
		cout<<"r="<<r<<endl;
		cout<<"area="<<area()<<endl;
	}
	double area()
	{
		return r*r*PI;
	}
	friend ostream& operator<<(ostream& os,test t)
	{
		t.output();
		return os;
	}
	bool operator>(test t)
	{
		return area()>t.area();
	}
	test operator++()//++i
	{
		//++r;
		//可以增加10
		r=r+10;
		return *this;
	}
	test operator++(int w)//i++
	{
		test old(*this);
		++(*this);
		return old;
	}
};

int main()
{
	test t1(10,10,20),t2(10,10,30);
//	t.output;
	if(t1>t2)
		cout<<t1;
	else 
		cout<<t2;
		
	t2=++t1;
	t2=t1++;
	cout<<t1;
	cout<<t2;
	return 0;
}
2、

​ 线段类,数据为两个端点的坐标x1、y1和x2、y2,内含构造函数(参数初始化)、数据输出函数(输出两个端点的坐标)。
​ 在类中对输出运算符进行重载,输出两个端点的坐标。对+运算符进行重载,返回一个线段,该线段的起点为第一条线段的起点,终点为第二条线段的终点。
​ 在主函数定义两个线段对象,相加后,使用“<<”输出数据。

#include <iostream>
using namespace std;

class test
{
protected:
	int x1,y1,x2,y2;
public:
	test(){x1=0;y1=0;x2=0;y2=0;}
	test(int xx1,int yy1,int xx2,int yy2)
	{
		x1=xx1;y1=yy1;x2=xx2;y2=yy2;
	}
	void output()
	{
		cout<<"first point:("<<x1<<","<<y1<<")"<<endl;
		cout<<"second point:("<<x2<<","<<y2<<")"<<endl;
	}
	test operator+(test t2)
	{
		test temp;
		temp.x1=x1;
		temp.y1=y1;
		temp.x2=t2.x2;
		temp.y2=t2.y2;
		return temp;
	}
	friend ostream& operator<<(ostream &os,test t)
	{
		t.output();
		return os;
	}
};

int main()
{
	test t1(1,1,2,2),t2(3,3,4,4);
	cout<<t1+t2;
	return 0;
}
3、上题程序

​ 在类中对>运算符进行重载,比较两条线段的长度。
​ 在主函数定义两个线段对象,用>比较长短,用 “<<”输出较长的线段数据。

//Ï߶ÎÀà 
#include <iostream>
using namespace std;

class test
{
protected:
	int x1,y1,x2,y2;
public:
	test(){x1=0;y1=0;x2=0;y2=0;}
	test(int xx1,int yy1,int xx2,int yy2)
	{
		x1=xx1;y1=yy1;x2=xx2;y2=yy2;
	}
	void output()
	{
		cout<<"first point:("<<x1<<","<<y1<<")"<<endl;
		cout<<"second point:("<<x2<<","<<y2<<")"<<endl;
	}
	bool operator >(test t)
	{
		int d1,d2;
		d1=(x2-x1)*(x2-x1)+(y2-y1)*(y2-y1);
		d2=(t.x2-t.x1)*(t.x2-t.x1)+(t.y2-t.y1)*(t.y2-t.y1);
		if(d1>d2)
			return true;
		else
			return false;
	}
	friend ostream& operator<<(ostream &os,test t)
	{
		t.output();
		return os;
	}
};

int main()
{
	test t1(1,1,2,2),t2(3,3,5,5);
	if(t1>t2)
		cout<<t1;
	else
		cout<<t2;
	return 0;
}
4、找零钱的问题

​ 币值有5元、2元、1元三种。要求找回的硬币数量最少。
​ 类中有构造函数(确定零钱数)、数据输出函数(输出零钱的硬币数量)。
​ 在类中对==运算符进行重载,判断零钱的硬币数量是否相等。
​ 在主函数中定义两个类的对象,使用==判断,相等输出“Yes!”,否则输出“No!” 。

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

class test
{
private:
	int n;
public:
	test(int nn)
	{
		n=nn;
	}
	int output()
	{
		int sum=0,i,j;
		i=n/5;
		j=n%5;
		sum=sum+i;
		i=j/2;
		j=j%2;
		sum=sum+i;
		sum=sum+j;
		return sum;
	}
	operator==(test t)
	{
		return output()==t.output();
	}
};

int main()
{
	test t1(16),t2(18);
	if(t1==t2)
		cout<<"Yes!"<<endl;
	else
		cout<<"No!"<<endl;
	return 0;
}
5、提分程序。

​ 类中有构造函数(确定学生人数,定义动态数组a)、数据输入函数、数据输出函数。
​ 在类中对++运算符进行重载,给不及格的同学提分,60分的提到61分;不及格的同学提到60分。
​ 在主函数中定义一个类的对象,使用++赋值后,输出对象数据。要求不改变++的左右位置运算属性。

①动态数组
#include<iostream>
#include<cstdlib>
#include<ctime>
#include<string>
using namespace std;
class test
{
private:
	int n;
	int* a; 
public:
	test(int nn)
	{
		n=nn;
		a=new int[n];
	}
	void input()
	{
		int i;
		for(i=0;i<n;++i)
		{
			cin>>a[i];
		}
	}
	void output()
	{
		int i;
		for(i=0;i<n;++i)
		{
			cout<<a[i]<<endl;
		}
	}
	test& operator++()//++i
	{
		int i;
		for(i=0;i<n;++i)
		{
			if(a[i]==60)
				a[i]=61;
			else if(a[i]< 60)
				a[i]= 60;
			else
				a[i]=a[i];
		}
		return *this;
	}
/*	test operator++(int m)//i++//这个好
	{
		test old(*this);
		++(*this);
		return old;
	}*/
	test operator++(int m)//i++
	{
		test old(3);
		int i;
		for(i=0;i<n;++i)
		{
			old.a[i]=a[i];
		}
		++(*this);
		return old;
	}
	test& operator=(const test& t)//жиди= 
	{
		if(a!=t.a)
		{
			n=t.n;
			for(int i=0;i<n;i++)
			{
				a[i]=t.a[i];
			}
		}
		return *this;
	}
/*	~test()
	{
		delete(a);
	}*/
};

int main()
{
	test t1(3),t2(3);
	t1.input();
	t2=t1++;//жиди++КЭ =
	t1.output();
	t2.output();
	return 0;
}

​ 在运算符重载的函数里面,局部变量test t(n),在return t的时候,这个局部变量会被销毁,调用一次~test(),这时候int* a的空间就被回收了。然后, cpp会调用一个copy constructor,生成一个对象,把这个对象返回给main里面的t2。因为并没有写自己的copy constructor,编译器帮助生成了一个,这个copy constructor仅仅做浅拷贝,也就是说t2对象里的a这个指针,指向的是那个局部变量里面 已经销毁 的那个a。对这个已经销毁的a,在最后退出main的时候,又会做一次delete,重复delete就出问题了。

②容器
//容器
#include <iostream>
#include <cstdlib>
#include <vector>
using namespace std;
class test
{
private:
	int n;
	vector <int> a;
public:
	test(int nn)
	{
		n=nn;
		int i,temp; 
		for(i=0;i<n;++i)
		{
			cout<<"input "<<i+1<<" :";
			cin>>temp;
			a.push_back(temp);
		}
	}
	void output()
	{
		int i; 
		cout<<"----------------"<<endl;
		for(i=0;i<n;++i)
		{
			cout<<"output "<<i+1<<" :"<<a[i]<<endl;
		}
	}
	test operator++()//++i
	{
		int i;
		for(i=0;i<n;++i)
		{
			if(a[i]==60)
				a[i]=61;
			else if(a[i]<60)
				a[i]=60;
		}
		return *this;
	}
	test operator++(int m)//i++ 
	{
		test t=*this;
		int i;
		for(i=0;i<n;++i)
		{
			if(a[i]==60)
				a[i]=61;
			else if(a[i]<60)
				a[i]=60;
		}
		return t;
	}
};
int main()
{
	test t1(3),t2(3);
	t2=t1++;
	t1.output();
	t2.output();
	return 0;
}

七、容器

(一)容器:vector

1、vector的定义与使用:顺序存储、下标访问
#include <iostream>
#include <cstdlib>
#include <string>
#include <ctime>
#include <vector>
#include <algorithm>//算法头文件
#include <numeric>//算法头文件
using namespace std;
//容器第一节课important!!!
//容器vector——向量

int main()
{
	vector<int>a;//a-容器名,调用和普通数组一样
	int i;
	srand(time(0));
	for(i=0;i<5;++i)
	{
		a.push_back(rand());//每写一个压一个数
	}
	a.push_back(rand());
	for(i=0;i<a.size();++i)//自动计算容器里有多少个数
	{
		cout<<a[i]<<endl;
	}
	a.erase(a.begin());//删掉容器第一个元素
	cout<<"========================="<<endl;
	for(i=0;i<a.size();++i)
	{
		cout<<a[i]<<endl;
	}
	return 0;
}
2、vector作形参:在数组作函数参数的那个例子基础上演绎
#include <iostream>
#include <cstdlib>
#include <string>
#include <ctime>
#include <vector>
#include <algorithm>//算法头文件
#include <numeric>//算法头文件
using namespace std;
//容器第一节课important!!!
//容器vector——向量

int sum(vector<int>a)
{
	int i,temp=0;
	for(i=0;i<a.size();++i)
	{
		temp=temp+a[i];
	}
	return temp;
}

int main()
{	
	vector<int>a[5];
	int i,temp;
	for(i=0;i<5;++i)
	{
		cout<<"input a["<<i<<"]:";
		cin>>temp;
		a.push_back(temp);
	}
	cout<<"sum"<<sum(a)<<endl;
	return 0;
}
3、四个函数
(1).push_back()
(2).size()
(3).begin()
(4).end()——容器中最后一个数据的下一个

begin和end成员

​ begin和end操作产生指向容器内第一个元素和最后一个元素的下一个位置的迭代器。这两个迭代器通常用于标记包含容器中所有元素的迭代范围。

​ c.begin() 返回一个迭代器,它指向容器c的第一个元素

​ c.end() 返回一个迭代器,它指向容器c的最后一个元素的下一个位置

​ c.rbegin() 返回一个逆序迭代器,它指向容器c的最后一个元素

​ c.rend() 返回一个逆序迭代器,它指向容器c的第一个元素前面的位置

实例
1、抽奖程序:.erase()

​ 从n人中随机抽取m人获奖。

#include <iostream>
#include <cstdlib>
#include <string>
#include <ctime>
#include <vector>
#include <algorithm>//算法头文件
#include <numeric>//算法头文件
using namespace std;

class test
{
private:
	int n,m,i,temp;
	vector<int>a;
public:
	test(int nn,int mm)
	{
		n=nn;m=mm;
		for(i=1;i<=n;++i)
		{
			a.push_back(i);
		}
	}
	void browse()
	{
		for(i=0;i<a.size();++i)
		{
			cout<<a[i]<<endl;
		}
	}
	void prize()
	{
		srand(time(0));
		for(i=0;i<m;++i)
		{
			temp=rand()%a.size();
			cout<<a[temp]<<endl;
			a.erase(a.begin()+temp);//删除a[temp]
		}
	}
};
int main()
{
	test t(5,2);
	t.prize();
	cout<<"========================"<<endl;
	t.browse();
	return 0;
}
2、加分程序:包含容器的对象作为函数返回值

​ 有n名学生,60分的提到61分,不及格的提到60分。
​ 在类内对++运算符进行重载,完成提分的功能。

#include <iostream>
#include <cstdlib>
#include <string>
#include <ctime>
#include <vector>
#include <algorithm>//算法头文件
#include <numeric>//算法头文件
using namespace std;
//提分程序
class test
{
private:
	vector<int>a;
	int n,i,temp;
public:
	test(int nn)
	{
		n=nn;
	}
	void input()
	{
		for(i=0;i<n;++i)
		{
			cin>>temp;
			a.push_back(temp);
		}
	}
	void output()
	{
		int n=a.size();
		for(i=0;i<n;++i)
		{
			cout<<a[i]<<endl;
		}
	}
	test operator++()//++i
	{
		for(i=0;i<a.size();++i)
		{
			if(a[i]==60) a[i]=61;
			else if(a[i]<60) a[i]=60;
		}
		return *this;
	}
	test operator++(int w)//i++
	{
		test old=*this;
		++(*this);
		return old;
	}
};

int main()
{
	test t1(3),t2(3);
	t1.input();
//	t2=++t1;
	t2=t1++;
	t1.output();
	t2.output();
	return 0;
}
数组与容器排序效率对比(在一个类中实现,各定义一个数组和一个容器)
1、计时器(不单独举例):clock()
#include <iostream>
#include <ctime>
#include <cstdlib>
using namespace std;
const long n=10000;

int main()
{
	clock_t t1,t2;//
	long i;
	int a[n];
	srand(time(0));
	t1=clock();
	for(i=0;i<n;++i)
	{
		a[i]=rand();
	}
	for(i=0;i<n;++i)
	{
		cout<<a[i]<<endl;
	}
	t2=clock();//
	cout<<"time:"<<t2-t1<<endl;//
	return 0;
}
2、C数组排序(用类实现):简单算法(选择法)
3、 cpp数组排序(算法:algorithm):sort(a,a+n,cmp)
4、vector排序:sort(a.begin(),a.end(),cmp)

c.begin() 返回一个迭代器,它指向容器c的第一个元素

c.end() 返回一个迭代器,它指向容器c的最后一个元素的下一个位置

5、排序规则函数:类内static函数
image-20210404162503930

sort1-从小到大排序

sort2-从大到小排序

#include <iostream>
#include <cstdlib>
#include <string>
#include <ctime>
#include <vector>
#include <algorithm>//算法头文件//排序
#include <numeric>//算法头文件
using namespace std;
const int n=100;
//排序数组,容器

class test
{
private:
	int a1[n];
	vector<int>a2;
	int i,j,k,temp;
public:
	test()
	{
		srand(time(0));
		for(i=0;i<n;++i)
		{
			a1[i]=rand();
			a2.push_back(rand());
		}
	}
	void browse1()
	{
		for(i=0;i<10;++i)
		{
			cout<<a1[i]<<endl;
		}
	}	
	void browse2()
	{
		for(i=0;i<10;++i)
		{
			cout<<a2[i]<<endl;
		}
	}	
    void sort1_1()//大计数组排序
	{
		for(i=0;i<n;++i)
		{
			k=i;
			for(j=i+1;j<n;++j)
			{
				if(a1[k]<a1[j])
					k=j;
			}
			temp=a1[i];
			a1[i]=a1[k];
			a1[k]=temp;
		}
	}
    static bool cmp(int n1,int n2)//排序规则函数——由大到小
	{
		return n1>n2;
	}
	void sort1_2()//<algorithm>数组排序	
	{
//		sort(a1,a1+n);//由小到大排
		sort(a1,a1+n,cmp);//由大到小排
	}
	void sort2()//容器排序
	{
		sort(a2.begin(),a2.end(),cmp);
	}
};

int main()
{
	clock_t tt;
	test t;
	tt=clock();
//	t.sort1_1();
//	t.browse1();

//	t.sort1_2();
//	t.browse1();
	
	t.sort2();
	t.browse2();
	cout<<"tiem of simple:"<<clock()-tt<<endl;
//	cout<<"======================"<<endl;
//	t.browse2();
	return 0;
}

vector的缺点:插入与删除开销较大,整体搬家

//结构体排序
#include <iostream>
#include <cstdlib>
#include <string>
#include <ctime>
#include <vector>
#include <algorithm>//算法头文件//排序
#include <numeric>//算法头文件
using namespace std;
const int n=100;

struct S
{
	int number;
	int score;
};

class test
{
private:
	vector<S>a;
	int i;
	S t;
public:
	test()
	{
		srand(time(0));
		for(i=0;i<n;++i)
		{
			t.number=rand();
			t.score=rand();
			a.push_back(t);
		}
	}
	void browse()
	{
		for(i=0;i<n;++i)
		{
			cout<<a[i].number<<"-"<<a[i].score<<endl;
		}
	}
    static bool cmp(S s1,S s2)//排序规则函数——由大到小
	{
		return s1.score>s2.score;
	}
	void Sort()//容器排序//大写S与sort区分开
	{
		sort(a.begin(),a.end(),cmp);
	}
};

int main()
{
	test t;
	t.Sort();
	t.browse();
	return 0;
}
7、迭代器:简单元素:输入(使用i)、遍历(使用p)、排序
①简单元素类型
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <vector>
#include <algorithm>
#include <numeric>
using namespace std;
const int n=100;

class test
{
private:
	vector<int>a;
	vector<int>::iterator p;
public:
	test()
	{
		int i;
		srand(time(0));
		for(i=0;i<n;++i)
		{
			a.push_back(rand());
		}
	}
	void browse()
	{
		for(p=a.begin();p!=a.end();++p)
		{
			cout<<*p<<endl;//容器里是简单数据类型加个*就行了
		}
	}
    static bool cmp(int n1,int n2)
	{
		return n1>n2;
	}
	void Sort()
	{
		sort(a.begin(),a.end(),cmp);
	}
};
int main() 
{
	test t;
	t.Sort();
	t.browse();
	return 0;
}
②复合元素类型
#include <iostream>
#include <cstdlib>
#include <string>
#include <ctime>
#include <vector>
#include <algorithm>//算法头文件//排序
#include <numeric>//算法头文件
using namespace std;
const int n=1000000;
//结构体排序——迭代器

struct S
{
	int number;
	int score;
};

class test
{
private:
	vector<S>a;
	vector<S>::iterator p,p_max;//迭代器——不再使用下标调用容器
	int i;
	S t;
public:
	test()
	{
		srand(time(0));
		for(i=0;i<n;++i)
		{
			t.number=rand();
			t.score=rand();
			a.push_back(t);
		}
	}
	
	void browse()//使用迭代器
	{
		for(p=a.begin();p!=a.end();++p)
		{
			cout<<p->number<<"-"<<p->score<<endl;//复合函数类型用p->...
		}
	}
    static bool cmp(S s1,S s2)//排序规则函数——由大到小
	{
		return s1.score>s2.score;
	}
	void Sort()//容器排序//大写S与sort区分开
	{
		sort(a.begin(),a.end(),cmp);
	}
	void max1()//找最大
	{
		Sort();
		p=a.begin();
		cout<<p->number<<"-"<<p->score<<endl;
	}
	void max2()//手工找最大
	{
		p_max=a.begin();
		for(p=a.begin();p!=a.end();p++)
		{
			if(p_max->score<p_max->score) p_max=p;
		}
		cout<<p_max->number<<"-"<<p_max->score<<endl;
	}
	void max3()//从小到大——函数法//速度最快
	{
		p_max=max_element(a.begin(),a.end(),cmp);
	}
};

int main()
{
	test t;
	clock_t tt;
//	t.Sort();
//	t.browse();
	cout<<"==========================="<<endl;
	tt=clock();
	t.max1();
	cout<<"time1:"<<clock()-tt<<endl;
	cout<<"==========================="<<endl;
	tt=clock();
	t.max2();
	cout<<"time2:"<<clock()-tt<<endl;
	cout<<"==========================="<<endl;
	tt=clock();
	t.max3();
	cout<<"time3:"<<clock()-tt<<endl;
	return 0;
}
练习(使用容器和迭代器):求最大值:手工、排序、算法
vector容器:学生成绩管理。

​ 每名学生有两项数据:学号、成绩,均为int类型数据。
​ 建立学生类,构造函数确定人数n,输入n个学生的数据。在类中编写一个函数,输出最高分与最低分的学生数据。
​ 要求使用容器和迭代器实现。
​ 解:三种方法:手工、排序、算法

#include <iostream>
#include <cstdlib>
#include <ctime>
#include <vector>
#include <algorithm>
#include <numeric>
using namespace std;

struct S
{
	int number;
	int score;
};

class test
{
private:
	vector<S>a;
	vector<S>::iterator p,p1,p2;
	int n;
public:
	test(int nn)
	{
		n=nn;
		int i;
		S t;
		srand(time(0));
		for(i=0;i<n;++i)
		{
			t.number=rand();t.score=rand();
			a.push_back(t);
		}
	}
	void browse()
	{
		for(p=a.begin();p!=a.end();++p)
		{
			cout<<p->number<<"-"<<p->score<<endl;
		}
	}
    
    static bool cmp(S s1,S s2)
	{
		return s1.score>s2.score;
	}
	static bool cmp1(S s1,S s2)
	{
		return s1.score<s2.score;
	}
	void Sort()
	{
		sort(a.begin(),a.end(),cmp);
	}
    
	void max_min_1()
	{
		p1=a.begin();p2=a.begin();
		for(p=a.begin();p!=a.end();++p)
		{
			if(p1->score<p->score) p1=p;
			if(p2->score>p->score) p2=p;
		}
		cout<<"max:"<<p1->number<<"-"<<p1->score<<endl;
		cout<<"min:"<<p2->number<<"-"<<p2->score<<endl;
	}
	void max_min_2()
	{
		Sort();
		p1=a.begin();p2=a.end()-1;
		cout<<"max:"<<p1->number<<"-"<<p1->score<<endl;
		cout<<"min:"<<p2->number<<"-"<<p2->score<<endl;
	}
	void max_min_3()
	{
		p1=max_element(a.begin(),a.end(),cmp1);
		p2=min_element(a.begin(),a.end(),cmp1);
		cout<<"max:"<<p1->number<<"-"<<p1->score<<endl;
		cout<<"min:"<<p2->number<<"-"<<p2->score<<endl;
	}
    
};

int main()
{
	test s(1000000);
//	s.browse();
	clock_t t;
	t=clock();
	s.max_min_1();
	cout<<"time1:"<<clock()-t<<endl;
	cout<<"========================"<<endl;
	t=clock();
	s.max_min_2();
	cout<<"time2:"<<clock()-t<<endl;
	cout<<"========================"<<endl;
	t=clock();
	s.max_min_3();
	cout<<"time3:"<<clock()-t<<endl;
	cout<<"========================"<<endl;
	return 0;
}

(二)list容器:序列式(双向链表)

复习vector容器——find查找函数
(1)单个元素
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <vector>
#include <algorithm>
#include <numeric>
using namespace std;
//复习上一节课讲的容器

class test
{
private:
	vector<int>a;
	vector<int>::iterator p;
	int n,i;
public:
	test(int nn)
	{
		n=nn;
		srand(time(0));
		for(i=0;i<n;++i)
		{
			a.push_back(rand());
		}
	}
	void browse()
	{
		for(p=a.begin();p!=a.end();++p)
		{
			cout<<*p<<endl;
		}
	}
    static bool cmp(int n1,int n2)//跟容器元素类型匹配
	{
		n1>n2;
	}
	void Sort()
	{
		sort(a.begin(),a.end());cmp);
	}
	void Find(int m)//对应容器元素类型
	{
		p=a.begin();//
		while(true)
		{
			p=find(p,a.end(),m);//(用谁找,查找范围,查找谁)
			if(p!=a.end())
			{
				cout<<*p<<endl;
				++p;//不要忘写++p;!!!!!!!!!!!!!!!!!!!!!!!!!!!!
			}
			else break;
		}
	}
};

int main()
{
	test t(100000);
//	t.Sort();
//	t.browse();
	t.Find(123);
	return 0;
}
(2)复合元素

可以选择从大到小或者从小到大(cmp),对谁排序(index)的方式:

#include <iostream>
#include <cstdlib>
#include <ctime>
#include <vector>
#include <algorithm>
#include <numeric>
using namespace std;

struct S
{
	int index;
	int number;
	int score;
	bool operator==(S s)//怎么处理Find函数//重载==
	//s是第二运算数第一个是自己
	{
		if(s.index==1)
			return number==s.number;
		else if(s.index==2)
			return score==s.score;
	}
};

class test
{
private:
	vector<S>a;
	vector<S>::iterator p;
	int n,i;
	S t;
public:
	test(int nn)
	{
		n=nn;
		srand(time(0));
		for(i=0;i<n;++i)
		{
			t.number=rand();	
            t.score=rand();
			a.push_back(t);
		}
	}
	void browse()
	{
		for(p=a.begin();p!=a.end();++p)
		{
			cout<<p->number<<"-"<<p->score<<endl;
		}
	}
    static bool cmp1(S s1,S s2)//跟容器元素类型匹配//学号排序
	{
		return s1.number>s2.number;//从大到小排序
	}
	static bool cmp2(S s1,S s2)//跟容器元素类型匹配//成绩排序
	{
		return s1.score>s2.score;//从大到小排序
	}
	void Sort()
	{
		sort(a.begin(),a.end(),cmp1);
	}
	void Find(int m)//对应容器元素类型
	{
		p=a.begin();
		t.index=2;//对成绩进行检索//1-学号
		t.score=m;//t.number=m;
        //上面两行一定要对应,否则结果不正确!!!
		while(true)
		{
			p=find(p,a.end(),t);//(用谁找,查找范围,查找谁)
			//需要解释查找的t是什么——要在结构体中重载==才能使用
			if(p!=a.end())
			{
				cout<<p->number<<"-"<<p->score<<endl;
				++p;
			}
			else break;
		}
	}
};

int main()
{
	test t(100000);
//	t.Sort();
//	t.browse();
	t.Find(123);
	return 0;
}

1、简单元素:整数
(1)插入与删除:比vector开销少!
(2)find函数:同vector
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <list>
#include <algorithm>
#include <numeric>
using namespace std;

class test
{
private:
	list<int>a;
	list<int>::iterator p;
	int n,i;
public:
	test(int nn)
	{
		n=nn;
		srand(time(0));
		for(i=0;i<n;++i)
		{
			a.push_back(rand());
		}
	}	
	void browse()
	{
		for(p=a.begin();p!=a.end();++p)
		{
			cout<<*p<<endl;
		}
	}
	void Find()
	{
		int temp;
		cout<<"input:";cin>>temp;
		p=a.begin();
		while(true)
		{
			p=find(p,a.end(),temp);
			if(p!=a.end())
			{
				cout<<*p<<endl;
				++p;
			}
			else break;
		}
	}
	void Erase()//删除元素//倒数第一个元素之前的元素
	{
		p=a.end();
		--p;--p;//倒数第一个元素之前的元素
		a.erase(p);//删除倒数第一个元素之前的元素
	}
	void Insert()
	{
		p=a.end();
		--p;
		a.insert(p,12345);//插到倒数第一个元素之前
	}
};
int main()
{
	test t(10);
	t.browse();
//	t.Find();
	cout<<"==============="<<endl;
	t.Erase();
	t.browse();
	cout<<"==============="<<endl;
	t.Insert();
	t.browse();
	return 0;
}
2、复合元素:结构体
(1)排序困难:不能用sort(与vector的区别)
(2)检索数据:同vector
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <list>//双向链表
#include <algorithm>
#include <numeric>
using namespace std;
//list容器

struct S
{
	int index;
	int number;
	int score;
	bool operator==(S s)//怎么处理Find函数
	//s是第二运算数第一个是自己
	{
		if(s.index==1)
			return number==s.number;
		else if(s.index==2)
			return score==s.score;
	}
};

class test
{
private:
	list<S>a;
	list<S>::iterator p;
	int n,i;
	S t;
public:
	test(int nn)
	{
		n=nn;
		srand(time(0));
		for(i=0;i<n;++i)
		{
			t.number=rand();	t.score=rand();
			a.push_back(t);
		}
	}
	void browse()
	{
		for(p=a.begin();p!=a.end();++p)
		{
			cout<<p->number<<"-"<<p->score<<endl;
		}
	}
    static bool cmp1(S s1,S s2)//跟容器元素类型匹配//学号排序
	{
		return s1.number>s2.number;//从大到小排序
	}
	static bool cmp2(S s1,S s2)//跟容器元素类型匹配//成绩排序
	{
		return s1.score>s2.score;//从大到小排序
	}
	void Find(int m)//对应容器元素类型
	{
		p=a.begin();
		t.index=2;//对成绩进行检索//1-学号
		t.score=m;//t.number=m;	
		while(true)
		{
			p=find(p,a.end(),t);//用谁找,查找范围,查找谁
			//需要解释查找的t是什么——要在结构体中重载==才能使用
			if(p!=a.end())
			{
				cout<<p->number<<"-"<<p->score<<endl;
				++p;//不要忘写
			}
			else break;
		}
	}
	void Del()//删除元素//倒数第一个元素之前的元素
	{
		p=a.end() ;
		--p;--p;//倒数第一个元素之前的元素
		a.erase(p); //删除倒数第一个元素之前的元素
	}
	void Insert()//插入元素//插到倒数第一个元素之前
	{
		p=a.end();
		--p;
		t.number=555;
		t.score=555;
		a.insert(p,t);//插到倒数第一个元素之前
	}
};

int main()
{
	test t(10);
//	t.Sort();	//双向链表不能排序
	t.browse();
	cout<<"===================================="<<endl;
	t.Del();
	t.Insert() ;
	t.browse();
//	t.Find(123);//list与vector是通用的
	return 0;
}

(三)map检索数据:关联式(二叉树)

1、map用法(同一键值只能录入一条记录,录入时按键值从小到大自动排序)
	map<key,data> a;
	map<key,data>::iterator p;
2、map检索:精确匹配
	p=a.find("003");//map自己的find,只针对键值进行检索
3、map元素类型
(1)<key,简单类型>
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <map>//
#include <algorithm>
#include <numeric>
using namespace std;	
//map容器

class test
{
private://map容器与之前的不一样,采用的是键值——第一项永远是键值
	map<int/*键值——里面的数据不能重复*/,int/*真实的类型*/>a;	
	map<int,int>::iterator p;
	int n,i;
public:
	test(int nn)
	{
		n=nn;
		srand(time(0));
		for(i=1;i<=n;++i)
		{
			a.insert(pair<int,int>(i/*保证里面的数据不重复*/,rand())/*数对的函数*/);//正向压
			//map压数据使用insert插入
            
//			a.insert(pair<int,int>(n-i/*保证里面的数据不重复*/,rand())/*数对的函数*/);
			//反向压——但存储的时候还是自动将里面数据按照键值从小到大排序——特点:检索速度快
		}
	}
	void browse()
	{
		for(p=a.begin();p!=a.end() ;++p)
		{
			cout<<p->first<<"-"<<p->second<<endl;
		}
	}
	void Find(int m)
	{
//		p=find(p,a.end(),m);//原来的
		p=a.find(m);//map特点:里头就写一个数就可以了//前面记得加“a.”——表示对谁进行寻找//只能查找键值
		if(p!=a.end())
		{
			cout<<p->first<<"-"<<p->second<<endl;
		}
		else cout<<"no record"<<endl;
	}
};

int main()
{
	test t(10);
//	t.browse() ;
	t.Find(5);
	return 0;
}
(2)<key,结构体>
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <map>
#include <algorithm>
#include <numeric>
using namespace std;
//map容器——复合元素

struct S
{
	int number;
	int score;
};
class test
{
private://map容器与之前的不一样,采用的是键值——第一项永远是键值
	map<int,S>a;	
	map<int,S>::iterator p;
	int n,i;
	S t;
public:
	test(int nn)
	{
		n=nn;
		srand(time(0));
		for(i=1;i<=n;++i)
		{
			t.number=rand();
			t.score=rand();
			a.insert(pair<int,S>(i,t));
		}
	}
	void browse()
	{
		for(p=a.begin();p!=a.end() ;++p)
		{
			cout<<p->first<<"-"<<p->second.number<<"-"<<p->second.score<<endl;
            //first——键值;second——真实的数据
		}
	}
	void Find(int m)
	{
		p=a.find(m);//map特点:里头就写一个数就可以了
		if(p!=a.end())
		{
			cout<<p->first<<"-"<<p->second.number<<"-"<<p->second.score<<endl;
		}
		else cout<<"no record"<<endl;
	}
};

int main()
{
	test t(10);
//	t.browse() ;
	t.Find(5);
	return 0;
}

map 中的键值可以是任何类型的。

4、map实例:英汉词典

练习

1、用vector实现滚动显示

​ vector元素类型为int,里面有5个元素,分别是5 个随机数。
​ 每按一次空格键,在容器尾追加一个随机数,同时将容器头的元素删除,保持里面有5个元素。按下ESC键程序结束。
​ 使用getch()识别按键值,ESC:27、空格:32
​ 清屏使用:system(“cls”);
​ 要求:在屏幕上滚动显示容器中的五个元素。

#include <iostream>
#include <cstdlib>
#include <ctime>
#include <vector>
#include <algorithm>
#include <numeric>
#include <conio.h>
using namespace std;

class test
{
private:
	vector<int>a;
	vector<int>::iterator p;
	int n,i;
	char c;
public:
	test(int nn)
	{
		n=nn;
		srand(time(0));
		for(i=0;i<n;++i)
		{
			a.push_back(rand());
		}
	}
	void browse()
	{
		for(p=a.begin();p!=a.end();++p)
		{
			cout<<*p<<endl;
		}
	}
	void output()
	{
		while(true)
		{
			c=getch();
			if(c==27)break;
			if(c==32)
			{
				p=a.begin() ;
				a.erase(p); 
				a.push_back(rand());
				system("cls");//清屏
				browse();
			}
		}
	}
};

int main()
{
	test t(5);
	t.browse() ;
	t.output() ;
	return 0;
}
2、list检索

​ list元素类型为结构体,里面有三项数据:学号(int)、姓名(string)、成绩(double)。
​ 检索成绩为某个分数以上的学生并输出他们的三项数据。
​ 要求使用find检索。

#include <iostream>
#include <cstdlib>
#include <list>
#include <numeric>
#include <string>
#include <algorithm>
using namespace std;

struct S
{
	int number;
	string name;
	double score;
	bool operator==(double d)
	{
		return (score>d);
	}
};

class test
{
private:
	list<S>a;
	list<S>::iterator p;
public:
	test()
	{
		S t;
		t.number=1;t.name="张一";t.score=87;a.push_back(t);
		t.number=2;t.name="张二";t.score=92;a.push_back(t);
		t.number=3;t.name="张三";t.score=75;a.push_back(t);
		t.number=4;t.name="张四";t.score=95;a.push_back(t);
	}
	void browse()
	{
		for(p=a.begin();p!=a.end();++p)
			cout<<p->number<<"-"<<p->name<<"-"<<p->score<<endl;
	}
	void Find(double d) 
	{
		p=a.begin();
		while(1)
		{
			p=find(p,a.end(),d); 
			if(p!=a.end())
			{
				cout<<p->number<<"-"<<p->name<<"-"<<p->score<<endl;
				++p;
			}
			else
			{
				break;
			}
		}
	}
};
int main()
{
	test t;
	t.browse();
	cout<<"=============="<<endl;
	t.Find(90);
	return 0;
}
3、map检索

​ map键值类型为字符串,表示学号;第二项元素类型为结构体,里面有两项数据:姓名(string)、成绩(double)。
​ 通过学号检索某个学生的信息,若没有检索到,输出“no person!”。
​ 要求使用find检索。

#include <iostream>
#include <cstdlib>
#include <string>
#include <map>
#include <algorithm>
#include <numeric>
using namespace std;

struct S
{
	string name;
	double score;	
};

class test
{
private:
	map<string,S>a;
	map<string,S>::iterator p;
public:
	test()
	{
		S t;
		t.name="张一";t.score=87;a.insert(pair<string,S>("001",t));
		t.name="张二";t.score=92;a.insert(pair<string,S>("002",t));
		t.name="张三";t.score=75;a.insert(pair<string,S>("003",t));
		t.name="张四";t.score=95;a.insert(pair<string,S>("004",t));
	}
	void browse()
	{
		for(p=a.begin();p!=a.end();++p)
			cout<<p->first<<"-"
				<<(p->second).name<<"-"
				<<(p->second).score<<endl;
	} 
	void Find(string s) 
	{
		p=a.find(s); 
		if(p!=a.end())
		{
			cout<<p->first<<"-"
				<<(p->second).name<<"-"
				<<(p->second).score<<endl;
		}
		else
		{
			cout<<"No person!"<<endl;
		}
	}
};

int main()
{
	test t;
	t.browse();
	cout<<"=============="<<endl;
	t.Find("003");
	cout<<"=============="<<endl;
	t.Find("005");
	return 0;
}

容器总结

(一)vector容器
1、定义
    vector<int> a;
    vector<int>::iterator p;
2、添加元素
	a.push_back(temp);
3、几个常用函数
(1)元素数量:a.size()
(2)首尾迭代器:a.begin()、a.end()
(3)删除元素:a.erase()
(二)list容器
1、简单元素:整数
(1)插入与删除:比vector开销少!
(2)find函数:同vector
(3)不能使用sort()函数排序
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <list>//双向链表
#include <algorithm>
#include <numeric>
using namespace std;
//list容器

struct S
{
	int index;
	int number;
	int score;
	bool operator==(S s)//怎么处理Find函数
	//s是第二运算数第一个是自己
	{
		if(s.index==1)
			return number==s.number;
		else if(s.index==2)
			return score==s.score;
	}
};

class test
{
private:
	list<S>a;
	list<S>::iterator p;
	int n,i;
	S t;
public:
	test(int nn)
	{
		n=nn;
		srand(time(0));
		for(i=0;i<n;++i)
		{
			t.number=rand();	t.score=rand();
			a.push_back(t);
		}
	}
	void browse()
	{
		for(p=a.begin();p!=a.end();++p)
		{
			cout<<p->number<<"-"<<p->score<<endl;
		}
	}
    static bool cmp1(S s1,S s2)//跟容器元素类型匹配//学号排序
	{
		return s1.number>s2.number;//从大到小排序
	}
	static bool cmp2(S s1,S s2)//跟容器元素类型匹配//成绩排序
	{
		return s1.score>s2.score;//从大到小排序
	}
	void Find(int m)//对应容器元素类型
	{
		p=a.begin();
		t.index=2;//对成绩进行检索//1-学号
		t.score=m;//t.number=m;	
		while(true)
		{
			p=find(p,a.end(),t);//用谁找;查找范围;查找谁
			//需要解释查找的t是什么——要在结构体中重载==才能使用
			if(p!=a.end())
			{
				cout<<p->number<<"-"<<p->score<<endl;
				++p;
			}
			else break;
		}
	}
	void Del()//删除元素//倒数第一个元素之前
	{
		p=a.end() ;
		--p;--p;
		a.erase(p); 
	}
	void Insert()//插入元素//插到倒数第一个元素之前
	{
		p=a.end();
		--p;
		t.number=555;
		t.score=555;
		a.insert(p,t);
	}
};

int main()
{
	test t(10);
//	t.Sort();	//双向链表不要排序
	t.browse();
	cout<<"===================================="<<endl;
	t.Del();
	t.Insert() ;
	t.browse();
//	t.Find(123);//list与vector是通用的
	return 0;
}
2、复合元素:结构体
(1)排序困难:不能用sort
(2)检索数据:同vector
(三)map容器
1、map用法
	map<key,data> a;
    map<key,data>::iterator p;
2、map检索:精确匹配
    p=a.find("003");//map自己的find,只针对键值进行检索
3、map元素类型
(1)<key,简单类型>
(2)<key,结构体>
(四)sort排序函数
1、 cpp数组排序(算法:algorithm):sort(a,a+n,cmp)
2、vector排序:sort(a.begin(),a.end(),cmp)

c.begin() 返回一个迭代器,它指向容器c的第一个元素

c.end() 返回一个迭代器,它指向容器c的最后一个元素的下一个位置

3、排序规则函数:类内static函数
image-20210404162503930

复合元素不能使用sort(struct)

(五)find查找函数
例子-vector
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <vector>
#include <algorithm>
#include <numeric>
using namespace std;

struct S
{
	int index;
	int number;
	int score;
	bool operator==(S s)//怎么处理Find函数
	//s是第二运算数第一个是自己
	{
		if(s.index==1)
			return number==s.number;
		else if(s.index==2)
			return score==s.score;
	}
};

class test
{
private:
	vector<S>a;
	vector<S>::iterator p;
	int n,i;
	S t;
public:
	test(int nn)
	{
		n=nn;
		srand(time(0));
		for(i=0;i<n;++i)
		{
			t.number=rand();	
            t.score=rand();
			a.push_back(t);
		}
	}
	void browse()
	{
		for(p=a.begin();p!=a.end();++p)
		{
			cout<<p->number<<"-"<<p->score<<endl;
		}
	}
    static bool cmp1(S s1,S s2)//跟容器元素类型匹配//学号排序
	{
		return s1.number>s2.number;//从大到小排序
        //不要忘记写return
	}
	static bool cmp2(S s1,S s2)//跟容器元素类型匹配//成绩排序
	{
		return s1.score>s2.score;//从大到小排序
	}
	void Sort()
	{
		sort(a.begin(),a.end(),cmp1);
	}
	void Find(int m)//对应容器元素类型
	{
		p=a.begin();
		t.index=2;//对成绩进行检索//1-学号
		t.score=m;//t.number=m;	
		while(true)
		{
			p=find(p,a.end(),t);//用谁找;查找范围;查找谁//
			//需要解释查找的t是什么——要在结构体中重载==才能使用
			if(p!=a.end())
			{
				cout<<p->number<<"-"<<p->score<<endl;
				++p;//不要忘了写
			}
			else break;
		}
	}
};

int main()
{
	test t(100000);
//	t.Sort();
//	t.browse();
	t.Find(123);
	return 0;
}
list使用方法同vector
map检索:精确匹配
    p=a.find("003");//map自己的find,只针对键值进行检索//前面有a.
⚠(六)find_if检索规则函数
⚠(七)for_each算法
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <vector>
#include <list>
#include <map>
#include <algorithm>
#include <numeric>
#include <conio.h>
using namespace std;

struct S
{
	int number;
	int score;
    /*
	bool operator==(S s)//检索规则函数
	{
		return score==s.score;
	}*/
};

class test
{
private:
	vector<S>a;
	vector<S>::iterator p;
	int n,i;
	S t;
public:
	test(int nn)
	{
		n=nn;
		srand(time(0));
		for(i=0;i<n;++i)
		{
			t.number=rand();t.score=rand();
			a.push_back(t);
		}
	}
	void browse()
	{
		for(p=a.begin();p!=a.end();++p)
		{
			cout<<p->number<<"-"<<p->score<<endl;
		}
	}
	static bool cmp(S s1,S s2)
	{
		return s1.score>s2.score;//不要忘记写return
	}
	void Sort()
	{
		sort(a.begin(),a.end(),cmp);
	}
    
	static bool find1(S s)
	{
		return s.number==s.score;
	}
	void Find(int m)
	{
		t.score=m;
		p=a.begin();
		while(true)
		{
//			p=find(p,a.end(),t);//需要一个检索规则函数
			p=find_if(p,a.end(),find1);//自己编一个检索函数find_if替代find
            //用什么找,查找范围,查找规则
			if(p!=a.end())
			{
				cout<<p->number<<"-"<<p->score<<endl;
				++p;//不要忘写++p;
			}
			else break;
		}
	}
    
	static void display(S s)
	{
		cout<<s.number<<"-"<<s.score<<endl;
	}
	static void add(S& s)//&——需要这个值带回来
	{
		s.score=s.score+100;
	}
	void output()
	{
		for_each(a.begin(),a.end(),display);//用display函数这个规则处理a.begin(),a.end()里的数
		cout<<"==============================================="<<endl;
		for_each(a.begin(),a.end(),add);//for_each——对容器里的元素进行处理
		for_each(a.begin(),a.end(),display);
	}
    
};

int main()
{
	test t(100000);
//	t.browse();
//	t.Sort();
//	t.Find(123);
	t.output() ;
	return 0;
}

八、计时器与多线程

容器复习与拓展

1、复习-三种容器的特点(实验总结)
①vector:顺序存储、下标调用,插入/删除开销较大;
②list:双向链表,插入/删除较为灵活;
③map:键值不重复,输入时排序,按键值检索效率较高。
//find-vector 
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <vector>
#include <algorithm>
#include <numeric>
#include <string>
using namespace std;

struct S
{
	int number;
	int score;	
	bool operator==(S s)
	{
		return score<s.score;
	}
};

class test
{
private:
	vector<S>a;	
	vector<S>::iterator p;	
	int n,i;
	S t;
public:
	test(int nn)
	{
		n=nn;
		srand(time(0));
		for(i=0;i<n;++i)
		{
			t.number=rand();t.score=rand();
			a.push_back(t);
		}
	}
	void Find(int m)
	{
		t.score=m;
		p=a.begin();
		while(true)
		{
			p=find(p,a.end(),t);
			if(p!=a.end())
			{
				cout<<p->number<<"-"<<p->score<<endl;
				++p;
			}
			else break;
		}
	}
};

int main()
{
	test t(1000);
	t.Find(123);
	return 0;	
}
2、拓展
①find_if检索规则函数
②for_each算法
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <vector>
#include <algorithm>
#include <numeric>
using namespace std;

struct S
{
	int number;
	int score;
	bool operator==(S s)//检索规则函数
	{
		return score==s.score;
	}
};

class test
{
private:
	vector<S>a;
	vector<S>::iterator p;
	int n,i;
	S t;
public:
	test(int nn)
	{
		n=nn;
		srand(time(0));
		for(i=0;i<n;++i)
		{
			t.number=rand();t.score=rand();
			a.push_back(t);
		}
	}
	void browse()
	{
		for(p=a.begin();p!=a.end();++p)
		{
			cout<<p->number<<"-"<<p->score<<endl;
		}
	}
	static bool cmp(S s1,S s2)
	{
		return s1.score>s2.score;//不要忘记写return
	}
	void Sort()
	{
		sort(a.begin(),a.end(),cmp);
	}
	static bool find1(S s)
	{
		return s.number==s.score;
	}
	void Find(int m)
	{
		t.score=m;
		p=a.begin();
		while(true)
		{
//			p=find(p,a.end(),t);//需要一个检索规则函数
			p=find_if(p,a.end(),find1);//自己编一个检索函数find_if替代find
			if(p!=a.end())
			{
				cout<<p->number<<"-"<<p->score<<endl;
				++p;//不要忘写++p;
			}
			else break;
		}
	}
	static void display(S s)
	{
		cout<<s.number<<"-"<<s.score<<endl;
	}
	static void add(S& s)//&——需要这个值带回来
	{
		s.score=s.score+100;
	}
	void output()
	{
		for_each(a.begin(),a.end(),display);//用display函数这个规则处理a.begin(),a.end()里的数
		cout<<"==============================================="<<endl;
		for_each(a.begin(),a.end(),add);//for_each——对容器里的元素进行处理
		for_each(a.begin(),a.end(),display);
	}
};

int main()
{
	test t(100000);
//	t.browse();
//	t.Sort();
//	t.Find(123);
	t.output() ;
	return 0;
}

(一)准备:

1、gotoxy函数
屏幕坐标系
#include <windows.h>
#include <iostream>
using namespace std;

/*
屏幕大小
0,0		79,0
0,24	79,24
*/

//光标移动到指定坐标处
void gotoxy(int x,int y)
{
	HANDLE h;//句柄,对象的索引
	COORD c;//结构体,坐标值
	c.X=x;
	c.Y=y;
	h=GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleCursorPosition(h,c);
}

int main()
{
	gotoxy(5,5);
	cout<<"hello";
	gotoxy(20,8);
	cout<<"world!";
	return 0;
}
2、其他函数:basic.cpp
//光标移动到指定坐标处
void gotoxy(int x,int y)
{
	HANDLE h;//句柄,对象的索引
	COORD c;//结构体,坐标值
	c.X=x;
	c.Y=y;
	h=GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleCursorPosition(h,c);
}

//隐藏光标
void hide_cursor()
{
	HANDLE	h_GAME = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_CURSOR_INFO cursor_info;
	GetConsoleCursorInfo(h_GAME,&cursor_info); 
	cursor_info.bVisible=false;					//不显示光标
	SetConsoleCursorInfo(h_GAME,&cursor_info); 
}

//显示光标
void show_cursor()				
{
	HANDLE	h_GAME = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_CURSOR_INFO cursor_info;
	GetConsoleCursorInfo(h_GAME,&cursor_info); 
	cursor_info.bVisible=true;					//显示光标
	SetConsoleCursorInfo(h_GAME,&cursor_info); 
}

//设置文本颜色
void color(int a)
{
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),a);
}

(二)计时器:

计时器工作原理
实例:秒表计时器
①单线程
//秒表计时器 
#include <windows.h>
#include <iostream>
#include <ctime>
#include <list>
#include <iomanip>//格式化输出头文件 
#include "basic.cpp"
using namespace std;

class test
{
private:
	int n;
	clock_t t;
public:
	test()	
	{
		n=0;
		t=clock();
		hide_cursor();
	}
	void move1()//timer
	{
		if(clock()-t>1000)
		{
			gotoxy(10,10);
			cout<<setw(3)<<setfill('0')<<n;//格式化输出 
			++n;
			t=clock();
		}
	}
	void move()
	{
		while(true)
		{
			move1();
		}
	}
	~test()
	{
		show_cursor();
	}
};
int main()
{
	test t;
	t.move();
	return 0;	
}

②多线程

​ 可移动移动的秒表计时器——见下三

(三)多线程(两线程)

多线程程序的流程图
1、原理:分时与巡检

​ 使用多个计时器,彼此独立计时,按各自的时间间隔执行各自代码。

2、实例:移动的秒表计时器

​ GetAsyncKeyState(VK_LEFT)

常用按键的VK值
#include <windows.h>
#include <iostream>
#include <ctime>//计时器
#include "basic.cpp"
using namespace std;
//秒表计时器
class test
{
private:
	clock_t t1,t2;
	int x,y;
	int n;
public:
	test()
	{
		n=0;
		x=10;y=10;
		t1=clock();
	}
	void draw()
	{
		gotoxy(x,y);
		cout<<n;
	}
	void erase()
	{
		gotoxy(x,y);
		cout<<"		";
	}
	void move1()//第一个计时器——时间动起来
	{
		if(clock()-t1>1000)//1000ms
		{
			draw();
			++n;
			t1=clock();//重新记录一下时间
		}
	}
	void move2()//第二个计时器——键盘控制
	{
		if(clock()-t2>50)//50ms
		{
			erase();//擦除轨迹
			if(GetAsyncKeyState/*识别键盘码函数*/(VK_ESCAPE/*ESC键*/))exit(0);
			if(GetAsyncKeyState(VK_UP/*上键*/))--y;
			if(GetAsyncKeyState(VK_DOWN/*下键*/))++y;
			if(GetAsyncKeyState(VK_LEFT/*左键*/))--x;
			if(GetAsyncKeyState(VK_RIGHT/*右键*/))++x;
			draw();//显示轨迹
			t2=clock();//重新记录时间//
		}
	}
	void Move()//主控程序
	{
		while(true)
		{
			move1();//第一个线程
			move2();//第二个线程
		}
	}
};

int main()
{
	test t;
	t.Move();
	return 0;
}

(四)实例:

1、自主运动的小球

​ 小球在屏幕中自下向上运动,到达上边界后循环到屏幕底部继续向上运动。

一线程。

//小球从下往上循环动
#include <windows.h>
#include <iostream>
#include <ctime>//计时器
#include "basic.cpp"
using namespace std;

class test
{
private:
	clock_t t;
	int x,y;
public:
	test()
	{
		x=10;y=10;
		t=clock();
	}
	void draw()
	{
		gotoxy(x,y);
		cout<<"● ";
	}
	void erase()
	{
		gotoxy(x,y);
		cout<<"   ";
	}
	void move1()
	{
		if(clock()-t>50)
		{
			erase();
			--y;
			if(y<0)y=24;//边界判断
			draw();
			t=clock();//不要忘了给t归零
		}
	}
	void Move()
	{
		while(true)
		{
			move1();
		}
	}
};

int main()
{
	test t;
	t.Move();
	return 0;
}
2、键盘控制小球运动

​ 方向键控制小球步进。

从零开始,两线程。

//键盘控制小球运动
#include <windows.h>
#include <iostream>
#include <ctime>//计时器
#include "basic.cpp"
using namespace std;

class test
{
private:
	clock_t t1,t2;
	int x,y;
public:
	test()
	{
		x=10;y=10;
		t1=clock();
		t2=clock();
	}
	void draw()
	{
		gotoxy(x,y);
		cout<<"● ";
	}
	void erase()
	{
		gotoxy(x,y);
		cout<<"   ";
	}
	void move1()//画小球
	{
		if(clock()-t1>50)
		{
			draw();
			t1=clock();//不要忘了给t归零
		}
	}
	void move2()//键盘控制
	{
		if(clock()-t2>50)//上擦下画
		{
			erase();
			if(GetAsyncKeyState(VK_ESCAPE))exit(0);
			if(GetAsyncKeyState(VK_UP))--y;
			if(GetAsyncKeyState(VK_DOWN))++y;
			if(GetAsyncKeyState(VK_LEFT))--x;
			if(GetAsyncKeyState(VK_RIGHT))++x;
			draw();
			t2=clock();
		}
	}
	void Move()//主控程序
	{
		while(true)
		{
			move1();
			move2();
		}
	}
};

int main()
{
	test t;
	t.Move();
	return 0;
}
3、键盘控制运动方向

​ 方向键控制小球运动方向,小球沿运动方向自主运动。

在上个程序的基础上修改,两线程。

//键盘控制小球运动方向
#include <windows.h>
#include <iostream>
#include <ctime>//计时器
#include "basic.cpp"
using namespace std;

class test
{
private:
	clock_t t1,t2;
	int x,y,c;
public:
	test()
	{
		x=10;y=10;
		t1=clock();
		t2=clock();
	}
	void draw()
	{
		gotoxy(x,y);
		cout<<"● ";
	}
	void erase()
	{
		gotoxy(x,y);
		cout<<"   ";
	}
	void move1()//小球前进程序
	{
		if(clock()-t1>50)//上擦下画
		{
			erase();
			switch(c)
			{
				case 1:--y;if(y<0)y=24;break;
				case 2:++y;if(y>24)y=0;break;
				case 3:--x;if(x<0)x=78;break;
				case 4:++x;if(x>78)x=0;break;
			}
			draw();
			t1=clock();//不要忘了给t归零
		}
	}
	void move2()//键盘控制
	{
		if(clock()-t2>50)
		{
			if(GetAsyncKeyState(VK_ESCAPE))exit(0);
			if(GetAsyncKeyState(VK_UP))c=1;
			if(GetAsyncKeyState(VK_DOWN))c=2;
			if(GetAsyncKeyState(VK_LEFT))c=3;
			if(GetAsyncKeyState(VK_RIGHT))c=4;
			t2=clock();//记录时间
		}
	}
	void Move()
	{
		while(true)
		{
			move1();
			move2();
		}
	}
};

int main()
{
	test t;
	t.Move();
	return 0;
}
⚠ 4、炮台程序(容器)

​ 键盘控制炮台移动,空格键向上发射炮弹。

从零开始,三线程。
注意:list容器删除元素时的处理

//发射炮弹
#include <windows.h>
#include <iostream>
#include <ctime>//计时器
#include "basic.cpp"
#include <list>
#include <algorithm>
using namespace std;

struct S//定义炮弹属性
{
	int x,y;
};

class test
{
private:
	clock_t t1,t2,t3;
	int x,y;
	list<S>a;
	list<S>::iterator p;
	S t;
public:
	test()
	{
		x=10;y=10;
		t1=clock();
		t2=clock();
		t3=clock();
	}
	void draw(int x,int y)
	{
		gotoxy(x,y);//不是类里面变量了,是函数的变量
		cout<<"● ";
	}
	void erase(int x,int y)
	{
		gotoxy(x,y);
		cout<<"  ";
	}
	void move1()//画小球
	{
		if(clock()-t1>50)
		{
			draw(x,y);
			t1=clock();//不要忘了给t归零
		}
	}
	void move2()
	{
		if(clock()-t2>50)//上擦下画//键盘设置
		{
			erase(x,y);
			if(GetAsyncKeyState(VK_ESCAPE))exit(0);
			if(GetAsyncKeyState(VK_UP))--y;
			if(GetAsyncKeyState(VK_DOWN))++y;
			if(GetAsyncKeyState(VK_LEFT))--x;
			if(GetAsyncKeyState(VK_RIGHT))++x;
			if(GetAsyncKeyState(VK_SPACE))
			{
				t.x=x;
				t.y=y-1;//向上发射一个炮弹
				a.push_back(t);//压进去一个炮弹
			}
			draw(x,y);
			t2=clock();
		}
	}
	void move3()//炮弹
	{
		if(clock()-t3>50)
		{
			for(p=a.begin();p!=a.end();/*++p迭代器会越界*/)
			{
				erase(p->x,p->y);
				--p->y;
				if(p->y<0)//到达边界
				{
					p=a.erase(p);//到最上面删掉炮弹//删除一个数据后迭代器会指向下一个
				}
				else
				{
					//看看有没有下一个炮弹
					draw(p->x,p->y);
					++p;//接下来for会画写一个炮弹的一个位置
				}
			}
			t3=clock();//记录一下时间
		}
	}
	void Move()
	{
		while(true)
		{
			move1();
			move2();
			move3();//通过while循环来画一枚炮弹的各个位置
		}
	}
};

int main()
{
	test t;
	t.Move();
	return 0;
}

练习

1、计数器

​ 在屏幕上某一位置显示一个数字,初始值为100。上下方向键对该数进行-1和+1的操作。左右方向键移动该数的位置。

//计数器
#include <windows.h>
#include <iostream>
#include <ctime>//计时器
#include "basic.cpp"
using namespace std;

class test
{
private:
	clock_t t1,t2,t3;
	int x,y;
	int n;
public:
	test()
	{
		x=10;
		y=10;
		n=100;
		t1=clock();
		t2=clock();
		t3=clock();
	}
	void draw()
	{
		gotoxy(x,y);
		cout<<n;
	}
	void erase()
	{
		gotoxy(x,y);
		cout<<"		";
	}
	void move1()
	{
		if(clock()-t1>50)
		{
			draw();
			t1=clock();
		}
	}
	void move2()
	{
		if(clock()-t2>50)
		{
			erase();
			if(GetAsyncKeyState(VK_ESCAPE))exit(0);
			if(GetAsyncKeyState(VK_UP))++n;
			if(GetAsyncKeyState(VK_DOWN))--n;
			draw();
			t2=clock();
		}
	}
	void move3()
	{
		if(clock()-t3>50)
		{
			erase();
			if(GetAsyncKeyState(VK_LEFT))--x;
			if(GetAsyncKeyState(VK_RIGHT))++x;
			draw();
			t3=clock();
		}
	}
	void move()
	{
		while(true)
		{
			move1();
			move2();
			move3();
		}
	}
};
int main()
{
	test t;
	t.move();
	return 0;
}
2、秒表计时器

​ 在屏幕上某一位置显示一个秒表计时器,初始值为0,其数值每秒钟递增1。方向键控制其移动。空格键数值归零。ESC键退出程序。

//秒表计时器
#include <windows.h>
#include <iostream>
#include <ctime>//计时器
#include "basic.cpp"
using namespace std;

class test
{
private:
	clock_t t1,t2;
	int x,y;
	int n;
public:
	test()
	{
		x=10;
		y=10;
		n=0;
		t1=clock();
		t2=clock();
	}
	void draw()
	{
		gotoxy(x,y);
		cout<<n;
	}
	void erase()
	{
		gotoxy(x,y);
		cout<<"		";
	}
	void move1()
	{
		if(clock()-t1>1000)
		{
			erase();
			++n;
			draw();
			t1=clock();
		}
	}
	void move2()
	{
		if(clock()-t2>50)
		{
			erase();
			if(GetAsyncKeyState(VK_UP))--y;
			if(GetAsyncKeyState(VK_DOWN))++y;
			if(GetAsyncKeyState(VK_LEFT))--x;
			if(GetAsyncKeyState(VK_RIGHT))++x;
			if(GetAsyncKeyState(VK_ESCAPE))exit(0);
			if(GetAsyncKeyState(VK_SPACE))n=0;
			draw();
			t2=clock();
		}
	}
	void move()
	{
		while(true)
		{
			move1();
			move2();
		}
	}
};

int main()
{
	test t;
	t.move();
	return 0;
}

九、 cpp字符串

复习:

1、容器检索
(1)有单一键值的数据检索,使用map<string,struct>处理:find
(2)其它检索,使用vector< struct >处理:find、遍历
2、复杂交互与计时器
(1)任意位置输出字符:gotoxy()
(2)键鼠响应:if(GetAsyncKeyState(VK_LEFT))
(3)计时器:clock()
(4)多线程:if(clock()-t>tt)
#include <windows.h>
#include <iostream>
#include <cstdlib>
#include <ctime>
#include "basic.cpp"
using namespace std;

class test
{
private:
	clock_t t1,t2;
	int x,y;
public:
	test()
	{
		x=10;y=10;
		t1=clock();
		t2=clock();
	}
	void draw()
	{
		gotoxy(x,y);
		cout<<"● ";
	}
	void erase()
	{
		gotoxy(x,y);
		cout<<"  ";
	}
	void move1()//ball
	{
		if(clock()-t1>50)
		{
			draw();
			t1=clock();
		}
	}
	void move2()//key
	{
		if(clock()-t2>50)
		{
			//上擦下画
			erase();
			if(GetAsyncKeyState(VK_ESCAPE))exit(0);
			if(GetAsyncKeyState(VK_UP))--y;
			if(GetAsyncKeyState(VK_DOWN))++y;
			if(GetAsyncKeyState(VK_LEFT))--x;
			if(GetAsyncKeyState(VK_RIGHT))++x;
			draw();
			t2=clock();
		}
	}
	void Move()//control
	{
		while(true)
		{
			move1();
			move2();
		}
	}
};
int main()
{
	test t;
	t.Move();
	return 0;
}

(一)定义与使用

1、定义
	string s2=s1;
	string s(10,'0');//"0000000000"
	//.size()//字符串实际长度
	//max_size()//字符串最大长度
2、+、>、<、==
//字符串:长度与连接
#include <iostream>
#include <string>
using namespace std;

int main()
{
	string s;
	cout<<"input string:";
	cin>>s;
	cout<<"the string is:"<<s<<endl;
	cout<<"the length is:"<<s.size()<<endl;
	cout<<"the added string is:"<<s+"123"<<endl;
//---------------------------------------------------
	cout<<"max length="<<s.max_size()<<endl;
	return 0;
}

(二)基本操作:6个函数

1、六个函数
(1)求长度:“.size()”".max_size()"方法
//s.size()
//s.max_size()
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <string>
#include <sstream>//字符串流
using namespace std;

int main()
{
/*	string s;
	cin>>s;
	cout<<s+"abcde"<<endl;
*/
	string s="1234567890";
//	string::size_type n;//标准的定义子串变量的方法
	cout<<s.size()<<endl;//求字符串长度
	cout<<s.max_size()<<endl;//最多能存多少
	return 0;
}
(2)删除子串:s.erase(开始位置,长度)
10-002-子串的位置
//s.erase
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <string>
#include <sstream>//字符串流
using namespace std;

int main()
{
	string s="1234567890";
	string::size_type n;//标准的定义子串变量的方法
	s=s.erase(3,2);//从第三个位置往后删除两个(位置 0,1,2,3,4)
	cout<<s<<endl;
	return 0;
}
(3)插入子串:s.insert(插入位置,”串”)
//s.insert
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <string>
#include <sstream>//字符串流
using namespace std;

int main()
{
	string s="1234567890";
	string::size_type n;//标准的定义子串变量的方法
	s=s.insert(3,"abc");//从第三个位置前插入(位置 0,1,2,3,4)
	cout<<s<<endl;
	return 0;
}
(4)替换子串:s.replace(开始位置,替换长度,替换串)
//s.replace
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <string>
#include <sstream>//字符串流
using namespace std;

int main()
{
	string s="1234567890";
	string::size_type n;//标准的定义子串变量的方法
	s=s.replace(3,2,"abc");//从第三个位置替换,替换2个,替换的内容(位置 0,1,2,3,4)
	cout<<s<<endl;
	return 0;
}
(5)查找子串:s.find(内容,开始位置)

​ 查找不到返回“string::npos”

//find查找子串位置
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <string>
#include <sstream>//字符串流
using namespace std;

int main()
{
	string s="1234567890";
	string::size_type n;//标准的定义子串变量的方法
//	n=s.find("45",0);//查找什么子串,从哪个位置开始查找(位置 0,1,2,3,4)
	//n——返回位置n
	if(s.find("45",0)!=string::npos)//查找什么子串,从哪个位置开始查找(位置 0,1,2,3,4)
	{
		n=s.find("45",0);
		cout<<n<<endl;
	}
	else cout<<"no"<<endl;
	return 0;
}
(6)取子串:s.substr(开始位置,长度);
//取子串
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <string>
#include <sstream>//字符串流
using namespace std;

int main()
{
	string s="1234567890",s1;
	string::size_type n;//自己定义类型
	s1=s.substr(3,2);//从哪个位置,取几个
	cout<<s1<<endl;
	return 0;
}
2、两个问题
1、位置变量类型
  string::size_type
2、使用find未检索到内容

返回string::npos常量

   if(s.find(s1,0)!=string::npos)
   {}

(三)实例

1、基本操作

​ 见下

2、找子串、取子串
①数据处理:简单类型
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <string>
#include <sstream>//字符串流
using namespace std;
//取出五个信息	-信息-
int main()
{
	string s="-1222-344-56-78-90-",s1;
	string::size_type n1,n2,pos=0;//定义三个位置变量
	while(true)
	{
		n1=s.find("-",pos);
		n2=s.find("-",n1+1);
		s1=s.substr(n1+1,n2-n1-1);//从哪个位置,取几个
		cout<<s1<<endl;
		pos=n2;
		if(pos+1>=s.size())//判断是否到结尾
        //结尾位置数+1=字符串长度
            break;
	}
	return 0;
}
②数据处理:复合类型
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <string>
#include <sstream>//字符串流
using namespace std;
//信息输出
int main()
{
	string s="-王小一-92-李小二-84-张小三-76-",s1/*姓名*/,s2/*成绩*/;
	string::size_type n1,n2,n3,pos=0;//定义三个位置变量
	while(true)
	{
		n1=s.find("-",pos);
		n2=s.find("-",n1+1);
		n3=s.find("-",n2+1);
		s1=s.substr(n1+1,n2-n1-1);//从哪个位置,取几个
		s2=s.substr(n2+1,n3-n2-1);
		cout<<s1<<endl;
		cout<<s2<<endl;
		pos=n3;
		if(pos+1>=s.size())break;//判断是否到结尾
		//结尾位置数+1=字符串长度
	}
	return 0;
}
3、模糊检索
//模糊检索 
#include <iostream>
#include <string>
using namespace std;

int main()
{
	string s="moon     n.月亮",s1,s2;
	string::size_type n1,n2;
	n1=s.find(" ",0);
	n2=n1;
	while(true)
	{
		if(s[n2]==' ')//注意这里使用''不是""
			++n2;
		else break;
	}
	s1=s.substr(0,n1);
	s2=s.substr(n2,s.size()-n2);
	cout<<s1<<endl;
	cout<<s2<<endl;
	return 0;
}
4、倒序

​ 字符串和数组联合处理

//字符串和数组联合处理
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <string>
#include <sstream>//字符串流
using namespace std;
//字符串倒置

string reverse(string s)//string后可以加&——节省内存空间
{
	string temp=s;//一定要初始化,给他分配与s一样大小的内存空间
	string::size_type i, n;
	n=s.size();
	for(i=0;i<n;++i)
	{
		temp[i]=s[n-i-1];
	}
	return temp;
}

int main()
{
	string s="今晚12点,图书馆门前见!";
	s=reverse(s);//倒序函数
	cout<<s<<endl;
	cout<<"==============================="<<endl;
	s=reverse(s);
	cout<<s<<endl;
	return 0;
}
5、变码
//字符串和数组联合处理
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <string>
#include <sstream>//字符串流
using namespace std;

string change1(string& s)//返回自己需要加&
{
	string::size_type i, n;
	n=s.size();
	for(i=0;i<n;++i)
	{
		s[i]=s[i]+5;
	}
	return s;//返回自己需要加&
}

string change2(string& s)//返回自己需要加&
{
	string::size_type i, n;
	n=s.size();
	for(i=0;i<n;++i)
	{
		s[i]=s[i]-5;
	}
	return s;//返回自己需要加&
}

int main()
{
	string s="今晚12点,图书馆门前见!";
	s=change1(s);
	cout<<s<<endl;
	cout<<"==============================="<<endl;
	s=change2(s);
	cout<<s<<endl;
	return 0;
}
6、英汉词条解析
//英汉词条解析 
#include <iostream>
#include <string>
#include <sstream>
using namespace std;

int main()
{
	string s="ability   n.能力",s1,s2;
	string::size_type n1,n2;
	n1=0;
	n2=s.find(" ",n1);
	s1=s.substr(n1,n2-n1);
	cout<<s1<<endl;
	//=====================
	n1=n2+1;
	while(true)
	{
		if(s[n1]==' ')
			++n1;
		else 
			break;
	}
	n2=s.size();
	s2=s.substr(n1,n2-n1);
	cout<<s2<<endl;
	return 0;
}

(四)练习

1、判断输入的是否是数字串

​ 从键盘输入一个字符串,判断该字符串是否由数字字符构成,若是则输出“yes!”,否则输出“no!”

ctype.h标准库头文件函数
//练习1
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <string>
#include <sstream>//字符串流
using namespace std;

class test
{
private:
	string s;
	string::size_type i,n;
public:
	test()
	{
		cout<<"input a string"<<endl;
		cin>>s;
	}
	void output()
	{
		bool flag=true;
		n=s.size();
		for(i=0;i<n;++i)
		{
			if(!isdigit(s[i]))//判断有没有数字字符//是个数字则返回真
			{
				flag=false;
				break;
			}
		}
		if(flag)
			cout<<"yes!"<<endl;
		else
			cout<<"no!"<<endl;
	}
};
int main()
{
	test t;
	t.output();
	return 0;	
}
2、(接1)字符串转数字

​ 从键盘输入一个字符串,判断该字符串是否由数字字符构成,若是则输出该数的平方,否则输出“no!”。

ctype.h标准库头文件函数
//练习2——字符串与数字的转换
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <string>
#include <sstream>//字符串流
using namespace std;

class test
{
private:
	string s;
	string::size_type i,n;
public:
	test()
	{
		cout<<"input a string"<<endl;
		cin>>s;
	}
	void output()
	{
		bool flag=true;
		n=s.size();
		for(i=0;i<n;++i)
		{
			if(!isdigit(s[i]))//判断有没有数字字符//是个数返回真
			{
				flag=false;
				break;
			}
		}
		if(flag)
		{
			cout<<"yes!"<<endl;
			cout<<str2num(s)+100<<endl;//转换字符串为数
		}
		else
			cout<<"no!"<<endl;
	}
	int str2num(string s)
	{
		stringstream ss;//定义一个字符串流
		int temp;
		ss<<s;//把s整进流去
		ss>>temp;//再把它捞出来(temp为int型,出来就是int型)
		return temp;
	}
};
int main()
{
	test t;
	t.output();
	return 0;	
}
3、统计子串数量

​ 从键盘输入一个字符串,统计该串中“ab”子串出现的次数。

//练习3——统计一个字符串里字串出现的次数
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <string>
#include <sstream>//字符串流
using namespace std;

class test
{
private:
	string s,s1;
	string::size_type n,pos;
public:
	test()
	{
		s="123ab456ab78ab999ab";
		s1="ab";
	}
	void output()
	{
		int sum=0;
		pos=0;
		while(true)
		{
			if(s.find(s1,pos)!=string::npos)
			{
				n=s.find(s1,pos);
				++sum;
				pos=n+s1.size();//得跳过s1
			}
			else break;
		}
		cout<<sum<<endl;
	}
};

int main()
{
	test t;
	t.output();
	return 0;	
}
4、字符串解析

​ 从键盘输入一个形如“-s1-s2-s3-”的字符串,将该字符串分解为三个子串s1、s2、s3,并且输出这三个子串。
​ 注意:s1、s2、s3为长度不等、内容不定的字符串。
​ 将解析出来的s1、s2、s3…子串压入容器,通过容器输出其中的所有元素。

#include <iostream>
#include <string>
#include <vector> 
using namespace std;

class test
{
private:
	string s;
	vector<string> a;
	vector<string>::iterator p;
public:
	test()
    {
        s="-123-1234-12345-";
    }	
	void input()
	{
		string::size_type pos,n;
		pos=0;
		while(true)
		{
			if(s.find("-",pos)!=string::npos)
			{
				pos=s.find("-",pos);
				n=++pos;//不要忘记往后窜一位,否则再找的还是这个“-”
				if(s.find("-",pos)!=string::npos)
				{
					pos=s.find("-",pos);
					a.push_back(s.substr(n,pos-n));
				}
				else break;
			}
			else break;
		}
	}
	void browse()
	{
		for(p=a.begin();p!=a.end();++p)
		{
			cout<<*p<<endl;
		}
	}
};
int main()
{
	test s;
	s.input();
	s.browse();
	return 0;
}

十、文本文件

复习

1、字符串的突破:定长(char a[30] [8];string a[30];)
#include <iostream>
#include <string>
#include <sstream>
using namespace std;

int main()
{
	// cpp字符串的优势——任意长度
	char a[30][8];//C
	string a[30];// cpp
	a[0]="11111111111111111111111111111";
	a[1]="222";

	//错误使用⬇
	string s;
	s[5]='1';//错误地使用
	
	return 0;
}
2、字符串赋值或开辟空间后才能用字符数组操作:reverse
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
//字符串复习
string reverse(string s)
{
	string::size_type i, n;
    n=s.size();
    string temp;
    temp.resize(n);//开辟空间
	for(i=0;i<n;++i)
	{
		temp[i]=s[n-i-1];
	}
	return temp;
}

int main()
{
	...
	return 0;
}
3、字符串与其他类型的转换:stringstream ss;
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
//字符串复习

int str2int(string s)//转换成数字
{
	int temp;
	//转换⬇
	stringstream ss;
	ss<<s;
	ss>>temp;
	//
	return temp;
}

int main()
{	
	string s="123";
	cout<<str2int(s)+100;
	return 0;
}

(一)文本文件与二进制文件的区别

1、区别
image-20210413231339613
2、文本文件的特点
(1)分行
(2)可识别字符(键盘常规输入)

(二)文本文件

1、基本操作(右图)
基本操作
3、写操作
(1)读/写:文件流、句柄、结束标志
(2)追加内容
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
using namespace std;
//写操作

int main()
{
	string s=/*"123456"*/"abcdefg";//再运行会被覆盖掉
//	ofstream/*o是out的意思*/ f1("f1.txt",ios::out/*决定是往文件里写*/);//定义一个写的文件变量
	ofstream f1("f1.txt",ios::app);//改成app就不会被覆盖掉了//追加内容
	if(f1)//判断成不成功——f1为真,可以写
	{
		f1<<s<<endl;
		f1.close();//关闭文件句柄
	}
	else//如果文件建立错误
		cout<<"file open error!"<<endl;
	return 0;
}
3、读取带空格的字符串:getline(f,s);
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
using namespace std;
//读操作

int main()
{
	string s1;
	ifstream/*i是in的意思*/ f2(/*"f1.txt"*/"11.3.cpp",ios::in);//定义一个读的文件变量
	if(f2)
	{
		/*
		f2>>s1;
		cout<<s1<<endl;
		f2>>s1;	//运行两遍把两行都读出来
		cout<<s1<<endl;
		f2.close();
		*/
		while(!f2.eof())//判断到没到文件尾
		{
//			f2>>s1;//>>识别换行和空格——导致都会换行
			getline(f2,s1);//不会识别空格了
			cout<<s1<<endl;
		}
	}
	else//如果文件建立错误
		cout<<"file open error!"<<endl;
	return 0;
}
3、文件名作参数:filename.c_str()
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
using namespace std;

void read_file(string filename)
{
	string temp;
	ifstream f(filename.c_str(),ios::in);/*把 cpp字符串转换成C字符数组*/
	while(!f.eof())
	{
		getline(f,temp);
		cout<<temp<<endl;
	}
}

int main()
{
	string filename;
	cout<<"input a filename:";cin>>filename;
	read_file(filename);//这里把文件名做成了参数
	return 0;
}
4、实例:读取并显示文本文件的内容
//读取并显示文本文件内容 
#include <iostream>
#include <string>
#include <fstream>
using namespace std;

void read_file(string filename)
{
	ifstream f(filename.c_str(),ios::in);
	string s;
	if(f)
	{
		while(!f.eof())
		{
			getline(f,s);
			cout<<s<<endl;
		}
		f.close();
	}
	else cout<<"open file error!"<<endl;
}
int main()
{
	string filename;
	cout<<"input filename:";
	cin>>filename;
	read_file(filename);
	return 0;
}

(三)实例:英汉词典

1、文件检索:右图(查找单词)
//实战1
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
using namespace std;
//键盘输入一个单词,在文本文件里找

class test
{
private:
	ifstream f;//☆
	string s,word,s1;
	string::size_type n;
public:
	test()
	{
		f.open("英汉词典.txt",ios::in);//上面有ifstream f;了就用f.open就行了
		if(!f)
		{
			cout<<"file open error!"<<endl;
		}
	}
	void dict()
	{
		cout<<"input a word:";cin>>word;
		//a		一个
		while(!f.eof())
		{
			getline(f,s);
			if(s.find(word,0)!=string::npos)//find(找啥,从哪里找)//找到那一行
			{
				n=s.find(" ",0);//先找位置
				s1=s.substr(0,n);//从哪里取,取多长(不是取到哪里)//把单词取出来
				if(s1==word)
				{
					cout<<s<<endl;
					break;
				}
			}
		}
	}
};

int main()
{
	test t;
	t.dict();
	return 0;
}
2、容器检索:map
//实战2
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <map>
#include <algorithm>
using namespace std;
//键盘输入一个单词,在文本文件里找
//放在map——检索的全程不在文件完成,在内存完成

class test
{
private:
	ifstream f;
	string s,word,s1;
	string::size_type n;
	map<string,string>a;
	map<string,string>::iterator p;
public:
	test()
	{
		f.open("英汉词典.txt",ios::in);
		if(!f)
		{
			cout<<"file open error!"<<endl;
		}
		else
		{
			while(!f.eof())
			{
				getline(f,s);
				n=s.find(" ",0);
				s1=s.substr(0,n);
				a.insert(pair<string,string>(s1,s));//把查的东西整成键值
			}
			f.close();
		}
	}
	void dict()
	{
		cout<<"input a word:";cin>>word;
		p=a.find(word);
		if(p!=a.end())
		{
			cout<<p->second<<endl;
		}
		else cout<<"no word"<<endl;
	}
};

int main()
{
	test t;
	t.dict();
	return 0;
}
3、单串检索
(1)分隔符的作用
(2)字符串解析
//实战3
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
using namespace std;
//键盘输入一个单词,在文本文件里找
//放在字符串里——检索的全程不在文件完成,在内存完成
//先整一个分隔符,但是“-”不可以了,因为有些单词里有“-”,所以使用“#”作为分隔符

class test
{
private:
	ifstream f;
	string s,word,s1,s2;
	string::size_type n1,n2,n3,pos;
public:
	test()
	{
		f.open("英汉词典.txt",ios::in);
		if(!f)
		{
			cout<<"file open error!"<<endl;
		}
		else
		{
			s="#";
			while(!f.eof())
			{
				getline(f,s1);
				s=s+s1+"#";
			}
			f.close();
		}
	}
	void dict()
	{
		//#moonlight		n.月光#
		//n1		n2			 n3
		cout<<"input a word:";cin>>word;
		pos=0;
		while(true)
		{
			if(s.find(word,pos)!=string::npos)
			{
				n1=s.find(word,pos);
				n1=s.rfind("#",n1);//反向查找
				n2=s.find(" ",n1);
				n3=s.find("#",n2);
				s1=s.substr(n1+1,n2-n1-1);//从哪里取,取多长//取单词
				s2=s.substr(n1+1,n3-n1-1);//取行串
				if(s1==word)
				{
					cout<<s2<<endl;
					break;
				}
				pos=n3;//不是继续找剩余的串eg.找的是moon不是moonlight	//迭代//不要忘了写
			}
			else break;
		}
	}
};

int main()
{
	test t;
	t.dict();
	return 0;
}
4、单串模糊检索(万能检索)
//实战4——模糊查找
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
using namespace std;
//键盘输入一个单词,在文本文件里找
//放在字符串里——检索的全程不在文件完成,在内存完成
//先整一个分隔符,但是“-”不可以了,因为有些单词里有“-”,所以使用“#”作为分隔符

class test
{
private:
	ifstream f;
	string s,word,s1,s2;
	string::size_type n1,n2,pos;
public:
	test()
	{
		f.open("英汉词典.txt",ios::in);
		if(!f)
		{
			cout<<"file open error!"<<endl;
		}
		else
		{
			s="#";
			while(!f.eof())
			{
				getline(f,s1);
				s=s+s1+"#";
			}
			f.close();
		}
	}
	void dict()
	{
		//#moonlight		n.月光#
		//n1		n2			  n3
		cout<<"input a word:";cin>>word;
		pos=0;
		while(true)
		{
			if(s.find(word,pos)!=string::npos)
			{
				n1=s.find(word,pos);
				n1=s.rfind("#",n1);//反向查找
				n3=s.find("#",n1+1);
//				n2=s.find(" ",n1);
//				n3=s.find("#",n2);
//				s1=s.substr(n1+1,n2-n1-1);//从哪里取,取多长//取单词
				s2=s.substr(n1+1,n3-n1-1);//取行串
//				if(s1==word)
//				{
					cout<<s2<<endl;
//					break;
//				}
				pos=n3;//不是继续找剩余的串eg.找的是moon不是moonlight	//迭代//不要忘了写
			}
			else break;
		}
	}
};

int main()
{
	test t;
	t.dict();
	return 0;
}

(四)练习

1、文本文件读取(数字)

​ 在文本文件“test.txt”中保存着一个数字,读取它并且求它的平方并输出。

#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
using namespace std;

class test
{
private:
	ifstream f;
//	int n;
	double n;
//	string n;
public:
	test()
	{
		f.open("f1.txt",ios::in);
		if(!f)
		{
			cout<<"file open error!"<<endl;
		}
	}
	void turn()
	{
		f>>n;//文件转化为整数//用流
		cout<<n*n<<endl;
	}
};

int main()
{
	test t;
	t.turn();
	return 0;
}
2、文件名作参数

​ 有两个文本文件:“t1.txt”和“t2.txt”,通过输入文件名,打开其中的一个文件并显示文件的内容。

#include <iostream>
#include <string>
#include <fstream>
using namespace std;

class test
{
private:
	string s;
	int n; 
	ifstream f;
	string filename; 
public:
	test()
	{
		cout<<"input filename:";
		cin>>s;
		f.open(s.c_str(),ios::in);	
		if(!f) cout<<"文件打开错误!"<<endl;
	}
	~test()
	{
		f.close();		
	}
	void turn()
	{
		string temp;
		cout<<"==============================="<<endl;
		while(!f.eof())
		{
			getline(f,temp);
			cout<<temp<<endl;
		}
		cout<<"==============================="<<endl;
	}
};
 
int main()
{
	test t;
	t.turn();
	return 0;
}
3、写文件

​ 从键盘输入一个数,求它的平方并输出,同时将结果写入文件“test.txt”中保存。若没有该文件则建立它,若有该文件则覆盖它。

#include <iostream>
#include <string>
#include <fstream>
using namespace std;

class test
{
private:
	string s;
	int n; 
	ofstream f;
	string filename; 
public:
	test()
	{
		f.open("test.txt",ios::out);	
		if(!f) cout<<"文件建立错误!"<<endl;
	}
	~test()
	{
		f.close();		
	}
	void turn()
	{
		cout<<"input a number:";
		cin>>n;
		cout<<"square:"<<n*n<<endl;
		f<<n*n;
	}
};

int main()
{
	test t;
	t.turn();
	return 0;
}
4、追加文件内容

​ 从键盘输入一个数,把它追加到文本文件“test1.txt”的末尾新行中。

#include <iostream>
#include <string>
#include <fstream>
using namespace std;

class test
{
private:
	string s;
	int n; 
	ofstream f;
	string filename; 
public:
	test()
	{
		f.open("test1.txt",ios::app);//这里	
		if(!f) cout<<"文件建立错误!"<<endl;
	}
	~test()
	{
		f.close();		
	}
	void turn()
	{
		cout<<"input a number:";
		cin>>n;
		f<<endl;//这里 
		f<<n<<endl;//这里 
	}
};
 
int main()
{
	test t;
	t.turn();
	return 0;
}
5、容器

​ 文本文件“test2.txt”中有多行数字(中间无空格),将这些数字读入到一个容器中,求它们的和并输出结果。

#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <vector>
using namespace std;

class test
{
private:
	ifstream f1;
	double n,sum;
	string s;
	vector<int>a;
	vector<int>::iterator p;
public:
	test()
	{
		f1.open("f1.txt",ios::in);
		if(!f1)
		{
			cout<<"file open error!"<<endl;
		}
		else
		{
			while(!f1.eof())
			{
				f1>>n;
				a.push_back(n); 
			}
		}
	}
	void turn()
	{
		for(p=a.begin();p!=a.end();++p)
		{
			sum=sum+*p;
		}
		cout<<sum<<endl;
	}
	~test()
	{
		f1.close();
	}
};

int main()
{
	test t;
	t.turn();
	return 0;
}

十一、二进制文件

复习:文本文件

(1)读:普通读、带空格读
	ifstream f(“test.txt”,ios::in)
    f>>s;
    getline(f,s);
(2)写:f<<s<<endl;
	ofstream f(“test.txt”,ios::out)
    f<<s;
//文本文件复习
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <vector>
using namespace std;
//写
class test
{
private:
	ofstream f;
	string s;
	int n;
public:
	test()
	{
		f.open("f1.txt",ios::app);
		if(!f)
		{
			cout<<"file open error"<<endl;
		}
		s="123";
		n=12345;
	}
	void turn()
	{
		//文本文件不区分输入的是字符串还是整数
		f<<s<<endl;
		f<<n<<endl;
	}
	~test()
	{
		f.close();
	}
};

int main()
{
	test t;
	t.turn();
	return 0;
}
(3)追加:ios::app
ofstream f(“test.txt”,ios::app)
    f<<s;

1、二进制文件基本操作

二进制文件基本操作二进制文件操作函数

string s;
fstream f("test.txt",ios::in|ios::out|ios::binary);//以读写方式打开文件
f.seekg(0,ios::end);//指向文件尾
string::size_type n=f.tellg();//文件长度
string s(n,' ');//s.resize(n);
f.seekg(0,ios::beg);//指向文件首
f.read((char*)s.c_str(),n);
(1)写
//二进制文件——写
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
using namespace std;

class test
{
private:
	fstream f;//二进制文件可以同时读写
	string s;
	int n;
public:
	test()
	{
		f.open("f1.txt",ios::in|ios::out|ios::binary/*二进制文件的意思*/);//三个缺一不可
		if(!f)
		{
			cout<<"file open error"<<endl;
		}
		s="123";
		n=12345;
	}
	void turn()
	{
//		f<<s<<endl;//二进制文件不可以这么写了
		
		f.seekp(0,ios::beg);//写——put;读get//二进制文件是移动指针(从哪写,)//0代表偏移量
		//写个字符串	//beg——begin文件开头
		f.write((char*)s.c_str(),s.size());//写啥//(char*)——转化为地址,c_str转化为s语言字符数组
		//(找位置,写多长)
		
		//写个整数——需要转化成字符串类型
		f.write((char*)&n,s.size());
	
	}
	~test()
	{
		f.close();
	}
};

int main()
{
	test t;
	t.turn();
	return 0;
}
(2)读
//二进制文件——读
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
using namespace std;

class test
{
private:
	fstream f;
	string s;
	string::size_type n;
public:
	test()
	{
		f.open("f1.txt",ios::in|ios::out|ios::binary);
		if(!f)
		{
			cout<<"file open error!"<<endl;
		}
	}
	void turn()
	{
		f.seekg(0,ios::end);//移动指针到文件尾部
		//ios::end是代表流的结尾,0代表偏移量
		n=f.tellg();//读那个文件的位置g——get
		s.resize(n);//分配空间
		f.seekg(0,ios::beg);//指针移动到文件头
		f.read((char*)s.c_str(),n);//读文件里头把n个字节字符串
		//==================================================================
		cout<<s<<endl;
	}
	~test()
	{
		f.close();
	}
};

int main()
{
	test t;
	t.turn();
	return 0;
}

2、实例

(1)倒序一个WORD文档

test.doc文件内容

​ 今晚12点,图书馆门前见,不见不散!

//实战——倒序
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
using namespace std;
//需要文件test.doc
//word文档倒序写回去

class test
{
private:
	fstream f;
	string s;
	string::size_type n;
public:
	test()
	{
		f.open("test.doc",ios::in|ios::out|ios::binary);
		if(!f)
		{
			cout<<"file open error!"<<endl;
		}
	}
	void turn()
	{
		//word文档倒序写回去
		f.seekg(0,ios::end);
		n=f.tellg();
		s.resize(n);
		f.seekg(0,ios::beg);
		f.read((char*)s.c_str(),n);
		//==============================
		s=reverse(s);
		//==============================
		f.seekp(0,ios::beg);
		f.write((char*)s.c_str(),n);
	}
	string reverse(string s)
	{
		string temp=s;//=s——必须给temp开辟空间
		string::size_type i;
		for(i=0;i<n;++i)
		{
			temp[i]=s[n-i-1];
		}
		return temp;
	}
	~test()
	{
		f.close();
	}
};

int main()
{
	test t;
	t.turn();
	return 0;
}
(2)切割一个WORD文档

test.doc文件内容

​ 今晚12点,图书馆门前见,不见不散!

//实战-切割
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
using namespace std;
//需要文件test.doc

class test
{
private:
	fstream f;
	string s,s1,s2;
	string::size_type n;
public:
	test()
	{
		f.open("test.doc",ios::in|ios::out|ios::binary);
		if(!f)
		{
			cout<<"file open error!"<<endl;
		}
	}
	void turn()
	{
		//word文档分割后写回去
		f.seekg(0,ios::end);
		n=f.tellg();
		s.resize(n);
		f.seekg(0,ios::beg);
		f.read((char*)s.c_str(),n);
		//==============================
//		s1=s.substr(0,100);//从0开始取100个字节//100不是位置是长度——实质从0取到99
//		s2=s.substr(100,n-100);
//		s=s2+s1;
		//============================
		s1=s.substr(0,n-100);//从0开始取100个字节//100不是位置是长度——实质从0取到99
		s2=s.substr(n-100,100);
		s=s2+s1;
		//==============================
		f.seekp(0,ios::beg);
		f.write((char*)s.c_str(),n);
	}
	~test()
	{
		f.close();
	}
};

int main()
{
	test t;
	t.turn();
	return 0;
}
(3)操作文本文件:英汉词典

​ 英汉词典程序。将文本文件“英汉词典.txt”用二进制方式打开,读入全部内容到一个字符串,从键盘输入一个单词,找到对应的词条并输出。

需要文件 英汉词典.txt

//实战-英汉词典
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
using namespace std;
//需要文件 英汉词典.txt

class test
{
private:
	fstream f;
	string s,s1,s2,word;
	string::size_type n,n1,n2,n3,pos;
public:
	test()
	{
		f.open("英汉词典.txt",ios::in|ios::out|ios::binary);
		if(!f)
		{
			cout<<"file open error!"<<endl;
		}
	}
	void turn()
	{
		f.seekg(0,ios::end);
		n=f.tellg();
		s.resize(n);
		f.seekg(0,ios::beg);
		f.read((char*)s.c_str(),n);
		//==============================
		cout<<"input word:";cin>>word;
		pos=0;
		//about		prep.关于
		//  n1
		while(true)
		{
			if(s.find(word,pos)!=string::npos)
			{
				n1=s.find(word,pos);
				n1=s.rfind("\n",n1);//找换行符
				n2=s.find(" ",n1);//找空格
				n3=s.find("\n",n2);
				s1=s.substr(n1+1,n2-n1-1);
				s2=s.substr(n1+1,n3-n1-1);
				if(s1==word)
				{
					cout<<s2<<endl;
					break;
				}
				pos=n3;
			}
			else 
			{
				cout<<"no word"<<endl;
				break;
			}
		}
	}
	~test()
	{
		f.close();
	}
};

int main()
{
	test t;
	t.turn();
	return 0;
}
(4)软件汉化(注意事项)

​ 将某个可执行文件中的英文信息转换为中文信息。

注意:

(1)备份好原文件,以免损坏后无法恢复;

(2)转换前后的字符串保持等长。为什么?

①原始文件
#include <iostream>
using namespace std;

int main()
{
	int n;
	cout<<"input a number:";
	cin>>n;
	cout<<"the square is:"<<n*n<<endl;
	system("pause");
	return 0;
}
②汉化文件
//实战——汉化文件
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
using namespace std;
//将原始程序the square is:汉化

class test
{
private:
	fstream f;
	string s;
	string::size_type n,n1;
public:
	test()
	{
		f.open("test1.exe",ios::in|ios::out|ios::binary);
		if(!f)
		{
			cout<<"file open error!"<<endl;
		}
	}
	void turn()
	{
		f.seekg(0,ios::end);
		n=f.tellg();
		s.resize(n);
		f.seekg(0,ios::beg);
		f.read((char*)s.c_str(),n);
		//====================================================
		n1=s.find("the square is:",0);//14个字符
		s.replace(n1,14,"这个数的平方:");//长度必须相同2*7
		//====================================================
		f.seekp(0,ios::beg);
		f.write((char*)s.c_str(),n);
	}
	~test()
	{
		f.close();
	}
};

int main()
{
	test t;
	t.turn();
	return 0;
}

3、练习

(1)拷贝文件

​ 将“test.txt”文件拷贝成“test1.txt”,使用二进制文件操作实现。

#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
using namespace std;
//练习1——二进制拷贝文件

class test
{
private:
	fstream f1,f2;
	string s;
	string::size_type n;
public:
	test()
	{
		f1.open("f1.txt",ios::in|ios::out|ios::binary);
		f2.open("f2.txt",ios::out|ios::binary);//没有这个文件要删掉ios::in|
		if(!f1||!f2)
		{
			cout<<"file open error!"<<endl;
		}
	}
	void turn()
	{
		f1.seekg(0,ios::end);
		n=f1.tellg();
		s.resize(n);
		f1.seekg(0,ios::beg);
		f1.read((char*)s.c_str(),n);
		//====================================================
		f2.seekp(0,ios::beg);
		f2.write((char*)s.c_str(),n);
	}
	~test()
	{
		f1.close();
		f2.close();
	}
};

int main()
{
	test t;
	t.turn();
	return 0;
}
(2)加解密文件(首尾交换)

​ 将“test.doc”文件的首尾字符进行交换。

#include <iostream>
#include <fstream>
using namespace std;

class test
{
private:
	fstream f;
	string s;
	string::size_type n;
public:
	test()
	{
		f.open("test.doc",ios::in|ios::out|ios::binary);
		if(!f) cout<<"打开文件错误!"<<endl;
	}
	~test()
	{
		if(f) {f.close();}
	}
	void turn()
	{
		f.seekg(0,ios::end);
		n=f.tellg();
		s.resize(n);
		f.seekg(0,ios::beg);
		f.read((char*)s.c_str(),n);
        //==================================
		char temp;
		temp=s[0];
		s[0]=s[n-1];
		s[n-1]=temp;
        //==================================
		f.seekp(0,ios::beg);
		f.write((char*)s.c_str(),n);
		cout<<"处理结束!"<<endl;
	}
};

int main()
{
	test t;
	t.turn();
	return 0;
}
(3)文件分解

​ 将“test.doc”文件的前10000字符放入“test1.doc”中,剩余字符放入“test2.doc”中。

#include <iostream>
#include <string>
#include <fstream>
using namespace std;

class test
{
private:
	fstream f,f1,f2;
	string s,s1,s2;
	string::size_type n;
public:
	test()
	{
		f.open("test.doc",ios::in|ios::out|ios::binary);
		f1.open("test1.doc",ios::out|ios::binary);
		f2.open("test2.doc",ios::out|ios::binary);
		if(!f||!f1||!f2)
		{
			cout<<"file open error!"<<endl;
			exit(0);
		}
	}
	void turn()
	{
		f.seekg(0,ios::end);
		n=f.tellg();
		s.resize(n);
		f.seekg(0,ios::beg);
		f.read((char*)s.c_str(),n);
		//==============================
		s1=s.substr(0,10000);
		s2=s.substr(10000,n-10000);
        //==============================
		f1.seekp(0,ios::beg);
		f1.write((char*)s1.c_str(),s1.size());
		f2.seekp(0,ios::beg);
		f2.write((char*)s2.c_str(),s2.size());
	}
	~test()
	{
		f.close();
		f1.close();
		f2.close();
	}
};
int main()
{
	test t;
	t.turn();
	return 0;
}
(4)文件合成(与3配套)

​ 将“test1.doc”和“test2.doc”中的内容合并到“test3.doc”中。

#include <iostream>
#include <fstream>
using namespace std;

class test
{
private:
	fstream f1,f2,f3;
	string s1,s2,s3;
	string::size_type n;
public:
	test()
	{
		f1.open("test1.doc",ios::in|ios::out|ios::binary);
		f2.open("test2.doc",ios::in|ios::out|ios::binary);
		f3.open("test3.doc",ios::out|ios::binary);
		if(!f1||!f2||!f3) cout<<"打开文件错误!"<<endl;
	}
	~test()
	{
		if(f1&&f2&&f3) {f1.close();f2.close();f3.close();}
	}
	void turn()
	{
		f1.seekg(0,ios::end);
		n=f.tellg();
		s1.resize(n);
		f1.seekg(0,ios::beg);
		f1.read((char*)s1.c_str(),n);
		//============================== 
		f2.seekg(0,ios::end);
		n=f.tellg();
		s2.resize(n);
		f2.seekg(0,ios::beg);
		f2.read((char*)s2.c_str(),n);
		//============================== 
		s3=s1+s2;
        //============================== 
		f3.seekp(0,ios::beg);
		f3.write((char*)s3.c_str(),s3.size());
		cout<<"处理结束!"<<endl;
	}
};

int main()
{
	test t;
	t.turn();
	return 0;
}
(5)变码

​ 将“test.doc”文件中的所有字符+5/-5。

//练习5——变码
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
using namespace std;

class test
{
private:
	fstream f;
	string s;
	string::size_type n,i;
public:
	test()
	{
		f.open("test.doc",ios::in|ios::out|ios::binary);
		if(!f)
		{
			cout<<"file open error!"<<endl;
		}
	}
	void turn()
	{
		f.seekg(0,ios::end);
		n=f.tellg();
		s.resize(n);
		f.seekg(0,ios::beg);
		f.read((char*)s.c_str(),n);
		//====================================================
		for(i=0;i<n;++i)
		{
			s[i]=s[i]+5;
		}
		//====================================================
		f.seekp(0,ios::beg);
		f.write((char*)s.c_str(),n);
	}
	~test()
	{
		f.close();
	}
};

int main()
{
	test t;
	t.turn();
	return 0;
}

十二、 cpp其它内容

复习:二进制文件(灵活地操作各种文件)

//复习——二进制形式操作文件
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
using namespace std;

class test
{
private:
	fstream f;
	string s;
	char c;
	string::size_type n;
public:
	test()
	{
		f.open("test1.exe",ios::in|ios::out|ios::binary);
		if(!f)
		{
			cout<<"file error!"<<endl;
		}
	}
	void turn1()//常规套路
	{
		f.seekg(0,ios::end);//读的指针移动到文件尾
		n=f.tellg();//告诉指针位置
		s.resize(n);//重新分配字符串s的空间
		f.seekg(0,ios::beg);
		f.read((char*)s.c_str(),n);//读取的是地址,写n个字节
		//=======================================================
		s[0]=s[0]+5;
		//=======================================================
		f.seekp(0,ios::beg);//写的指针移动到文件头
		f.write((char*)s.c_str(),n);
	}
	void turn2()//常规套路
	{
		f.seekg(0,ios::beg);//读的指针移动到文件头
		f.read(&c,1);//读取的是地址,写1个字节
		//=======================================================
		c=c+5;
		//=======================================================
		f.seekp(0,ios::beg);//写的指针移动到文件头
		f.write(&c,1);
	}
	void Look()//看看第一个字节是什么
	{
		f.seekg(0,ios::beg);//读的指针移动到文件头
		f.read(&c,1);//读取的是地址,写1个字节
		//=======================================================
		cout<<(int)c/*转化为ASCII码*/<<endl;
		//会发现所有的文件第一个字节都是77——M
		c='A';//把M改成A//程序还会坏掉
		//=======================================================
		f.seekp(0,ios::beg);//写的指针移动到文件头
		f.write(&c,1);
	}
};

int main()
{
	test t;
//	t.turn1();
//	t.turn2(); 
	t.Look();
	return 0;
}

(一)模板

1、函数模板与类模板

​ 实例见下

1、模板的含义
把类型作为参数!
2、模板的用法

template<class T>
class test
{
private:
   	T a,b;
};
2、类型的判断
	if(typeid(T1)==typeid(int))

​ 实例见下

3、实例
(1)函数模板
// cpp剩余部分1——函数模板
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
//vector<int>a;//<int>就是模板——把类型当作参数
//但是模板怎么做呢?

//函数重载——函数参量数量或类型不一样
/*
int add(int a,int b)
{
	return a+b;
}
double add(double a,double b)
{
	return a+b;
}
*/

//现在把上面这个做成函数模板
template<class T>//定义模板(这里是一项参数)
//多项参数——template<class T,class TT, ...>//都可以
T add(T a,T b)
{
	return a+b;
}

int main()
{
	cout<<add<int>(1,2)<<endl;//<int>——类型参数告诉函数的实际类型
	cout<<add<double>(1.5,2.3)<<endl;
	return 0;
}
(2)类模板
// cpp剩余部分1——模板
//实例——类模板
#include <iostream>
#include <string>
#include <sstream>
#include <typeinfo>//typeid头文件
using namespace std;

template<class T>
class test
{
private:
	T a,b;
public:
	test(T aa,T bb)
	{
		a==aa;b=bb;
	}
	void output()
	{
		if(typeid(T)==typeid(int))//typeid能判断<>里的类型//需要头文件#include <typeinfo>
		{
			cout<<a+b<<endl;
		}
		else if(typeid(T)==typeid(string))
		{
			cout<<str2int(a)+str2int(b)<<endl;//转换为整数后相加
		}
	}
	int str2int(/*string*/T s)
	/*这时候传进来的a,b是T类型,虽然实质上是string类型但是他不认*/
	{
		int temp;
		stringstream ss;
		ss<<s;
		ss>>temp;
		return temp;
	}
};

int main()
{
	test<int>/*在这里给类型*/ t1(1,2);
	test<string> t2("12","34");
	t1.output();
	t2.output();
	return 0;
}

(二)命名空间

1、用途:防止命名冲突
2、使用方法
(1)定义两个命名空间(里面有同名的类):
namespace ns1{}; 
namespace ns2{};
(2)定义对象
// cpp剩余部分2——命名空间
#include <iostream>
using namespace std;
//命名空间——防止命名冲突

namespace ns1//个人的命名空间,可以把自己所有的代码都扔进去
{
	class test
	{
	private:
		int m;
	public:
		test(int mm)
		{
			m=mm;
		}
		void output()
		{
			cout<<"m="<<m<<endl;
		}
	};
}

namespace ns2//在两个各自的命名空间,互相不影响
{
	class test
	{
	private:
		int m;//在两个各自的命名空间,互相不影响
	public:
		test(int mm)
		{
			m=mm;
		}
		void output()
		{
			cout<<"m="<<m<<endl;
		}
	};
}

int main()
{
	//如何使用命名空间
	ns1::test t1(100);//与ios::区分开,ios这个是类
	ns2::test t2(200);
	t1.output();
	t2.output();
	return 0;
}

⚠(三)虚函数

1、用途:使用基类指针调用子类同名函数
2、定义与使用
// cpp剩余部分3——虚函数
#include <iostream>
using namespace std;

class test
{
protected:
	int m;
public:
	test(int mm)
	{
		m=mm;
	}
	virtual void output()//同名函数做成虚函数
	{
		cout<<"m="<<m<<endl;
	}
};

class test1:public test
{
protected:
	int n;
public:
	test1(int mm,int nn):test(mm)
	{
		n=nn;
	}
	virtual void output()//同名函数做成虚函数
	{
		cout<<"n="<<n<<endl;
	}
};

int main()
{
	//用父类的指针调用子类的同名函数!!!!!!!!!!!!!!!!!!!!!!!!!!
	test1 t(100,200);
	//调用哪个output?
	//需要把output同名函数都做成虚函数

	test* p;//定义一个指针(父类)
	p=&t;//指针先定义一下子类的地址,防止飞掉

	/*可用下面代码代替上面代码
	test* p(this);//定义一个基类的指针让他指向子类
	*/

//出题方式:没做虚函数输出什么,做了虚函数输出什么?

	p->output();//如果类里面不改成虚函数,输出的还是基类的函数,跟指针的指向无关
    			//改成虚函数就与指针的指向有关了
//	t.output();
	return 0;
}
3、虚基类(抽象类)
// cpp剩余部分3——虚函数
//模板与虚函数考试考一道5分小题!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
#include <iostream>
using namespace std;

class test//虚基类
//没有具体的函数实现,只用类的框架函数声明,目的是给派成出子类里调用
{
protected:
	......
public:
	virtual void a1(int m,...);
	virtual void a2(int m,...);
	......
	virtual void an(int m,...);
};

void a1(...)//只在声明中加虚,具体实现的时候不加虚
{
	...
}

int main()
{
	
	return 0;
}

(四) cpp异常处理

正常使用,没有做异常处理

// cpp剩余部分4——异常处理
#include <iostream>
using namespace std;

//正常使用,没有做异常处理
class test
{
private:
	double m,n;
public:
	test(double mm,double nn)
	{
		m=mm;n=nn;
	}
	void ave()
	{
		cout<<m/n<<endl;
	}
};

int main()
{
	test t(10,6);
	t.ave();
	return 0;
}
1、手工抛出异常
// cpp剩余部分4——异常处理
#include <iostream>
using namespace std;

//做了异常处理
class test
{
private:
	double m,n;
public:
	test(double mm,double nn)
	{
		m=mm;n=nn;
	}
	void ave() throw(int)//抛出一个异常类型——int型
	{
		if(n>1)
			cout<<m/n<<endl;
		else if(n==1)
			throw 1;//抛出一个整数异常码1
		else if(n==0)
			throw 0;//抛出一个整数异常码0
	}
};

int main()
{
	test t1(10,6),t2(10,1),t3(10,0);
	//放在异常处理机里过一下筛子
	try
	{
		t1.ave();
		t2.ave();
		t3.ave();
	}
	//捕获一下异常
	catch(int e)
	{
		if(e==1)
			cout<<"divided by 1!"<<endl;
		else if(e==0)
			cout<<"divided by 0!"<<endl;
	}	//输出的是第一个出现错误的异常码,后面的不输出,把前面的改对后,后面的才会输出
	return 0;
}
2、使用异常类
// cpp剩余部分4——异常处理
//用异常类简化代码
//不考
#include <iostream>
#include <stdexcept>//标准异常类
using namespace std;

//做了异常处理
class test
{
private:
	double m,n;
public:
	test(double mm,double nn)
	{
		m=mm;n=nn;
	}
	void ave() throw(invalid_argument)//抛出一个异常类//invalid_argument相当一个容器一样
	{
		if(n>1)
			cout<<m/n<<endl;
		else if(n==1)
			throw invalid_argument("divided by 1!");//把错误信息扔到异常类里面
		else if(n==0)
			throw invalid_argument("divided by 0!");
	}
};

int main()
{
	test t1(10,6),t2(10,1),t3(10,0);
	//放在异常处理机里过一下筛子
	try
	{
		t1.ave();
		t2.ave();
		t3.ave();
	}
	//捕获一下异常
	catch(exception& e)//用异常类里的对象来读取
	{
		cout<<"error:"<<e.what()<<endl;//调用异常类里的函数
	}	//输出的是第一个出现错误的异常码,后面的不输出,把前面的改对后,后面的才会输出
	return 0;
}

(五)程序调序方法

1、使用编译器的调试功能
2、手工调试:条件编译

(1)#pragma once
(2)#define _DEBUG

#ifdef
此处编写调试语句
#endif

// cpp剩余部分5——预编译
#pragma once//1、发布程序前,防止头文件重复,
#include <iostream>
#include <stdexcept>//标准异常类
//#include "basic.cpp"
using namespace std;
//2、预编译调试开关
#define _DEBUG//定义一个开关,并打开这个开关
//把上面这个语句注释了,开关里面的语句就不会运行了

class test
{
private:
	double m,n;
public:
	test(double mm,double nn)
	{
		m=mm;n=nn;
#ifdef _DEBUG
		cout<<"tset::test()"<<endl;//查看这个函数是否运行
#endif
	}
	void ave() throw(invalid_argument)//抛出一个异常类//invalid_argument相当一个容器一样
	{
		if(n>1)
			cout<<m/n<<endl;
		else if(n==1)
			throw invalid_argument("divided by 1!");//把错误信息扔到异常类里面
		else if(n==0)
			throw invalid_argument("divided by 0!");
	}
	~test()
	{
#ifdef _DEBUG
		cout<<"tset::~test()"<<endl;//查看这个函数是否运行
#endif
	}
};

int main()
{
	test t(10,6);
#ifdef _DEBUG
	cout<<"================================================"<<endl;
#endif
	return 0;
}

十三、复习课

(一) cpp主要内容归纳

1、容器 √√√
(1)vector(test16-1)
  • 复合元素

  • 排序规则函数

  • 检索规则函数

//vector复合元素类型、排序规则函数、检索规则函数 
#include <iostream>
#include <vector>
#include <string>
#include <ctime>
#include <cstdlib>
#include <algorithm>
using namespace std;

struct S
{
	int number;
	int score;
//检索规则函数 
//====================================
	
	bool operator==(S s)//注意第一个参数是自己
	{
		return score==s.score;//注意双等号
	}

//====================================	
};

class test
{
private:
	vector<S>a;
	vector<S>::iterator p;	
	S t;
	int n;
public:
	test(int nn)
	{
		n=nn;
		srand(time(0));
		for(int i=0;i<n;++i)
		{
			t.number=rand();
			t.score=rand();
			a.push_back(t);
		}
	}
//排序规则函数 
//====================================
	
	static bool cmp(S s1,S s2)//注意static和bool
	{
		return s1.score>s2.score;
	}
	
//====================================	
	void browse()
	{
		for(p=a.begin();p!=a.end();++p)
		{
			cout<<p->number<<"-"<<p->score<<endl;
		}
	}
	void Sort()
	{
		sort(a.begin(),a.end(),cmp);
	}
	void Find(int m)
	{
		t.score=m;
		p=a.begin();
		while(true)
		{
			p=find(p,a.end(),t);
			if(p!=a.end())
			{
				cout<<p->number<<"-"<<p->score<<endl;
				++p;
			}
			else break;
		}
	}
};
int main()
{
	test t(100000);
	t.Sort();
	t.Find(123);
	return 0;
}
(2)map(test16-2)√√
  • 查找:英汉词典, a.find函数
//map检索:a.find函数 
#include <iostream>
#include <map>
#include <string>
#include <fstream>
#include <algorithm>
#include <ctime>
using namespace std;

class test
{
private:
		map<string,string>a;
		map<string,string>::iterator p;
		string s,s1,word;
		string::size_type n;
public:
	test()
	{
		string s,s1,s2;
		ifstream f("英汉词典.txt",ios::in);
		if(f)
		{
			while(!f.eof())
			{
				getline(f,s);
				n=s.find(" ",0);
				s1=s.substr(0,n);
				a.insert(pair<string,string>(s1,s));
			}
		}
		else cout<<"file error!"<<endl;
	}
	void Find()
	{
//====================================
		
		cout<<"input a word:";cin>>word;//输入单词
		p=a.find(word);//查键值
		if(p!=a.end())
		{
			cout<<p->second<<endl;
		}
		else cout<<"no word!"<<endl;
	
//====================================
	}
};
int main()
{
	test t;
	t.Find();
	return 0;
}
2、字符串与文件 √√√(test16-3)
(1)原则:把文件操作转换为字符串操作
  • 读取文件内容

  • 内存处理:字符串、容器

  • 写回文件

(2)文件的操作方式
  • 文本文件

  • 二进制文件

//字符串与文件:文件倒序 
#include <iostream>
#include <string>
#include <fstream>
using namespace std;

class test
{
private:
	fstream f;
	string s;
	string::size_type n,i;
public:
	test()
	{
		f.open("test.doc",ios::binary|ios::in|ios::out);
		if(!f)
		{
			cout<<"file error!"<<endl;
		}
	}	
	string reverse(string s)
	{
		string temp=s;
		for(int i=0;i<n;++i)
		{
			temp[i]=s[n-i-1];
		}
		return temp;
	}
	void turn()
	{
//====================================
	
		f.seekg(0,ios::end);//获取的指针定位到文件尾,偏移量为0
		n=f.tellg();//把文件长度读到n中来
		s.resize(n);//重新分配s空间
		f.seekg(0,ios::beg);//获取的指针定位到文件头,偏移量为0
		f.read((char*)s.c_str(),n);//获取内容//str后面不要忘写()
		//===========================================================
		s=reverse(s);//倒置
		Write();//写回去
	
//====================================	
	}
	void Write()
	{
		f.seekp(0,ios::beg);
		f.write((char*)s.c_str(),s.size());
	}	
	~test()
	{
		f.close();
	}
};

int main()
{
	test t;
	t.turn();
	return 0;
}
(3)数据库检索的思想
  • 精确查询:使用map
  • 模糊查询:使用字符串(大串)(test16-4)
//字符串:模糊检索 
#include <iostream>
#include <string>
#include <fstream>
using namespace std;

class test
{
private:
	fstream f;
	string s,s1,word;
	string::size_type n,n1,n2,pos;
public:
	test()
	{
		f.open("英汉词典.txt",ios::in|ios::out|ios::binary);
		if(!f)
		{
			cout<<"file open error!"<<endl;
			exit(0);
		}
		else
		{
			f.seekg(0,ios::end);
			n=f.tellg();
			s.resize(n);
			f.seekg(0,ios::beg);
			f.read((char*)s.c_str(),n);
		}
	}
	void dict()
	{
		cout<<"input a word:";cin>>word;
		pos=0;
		//out:about
		while(true)
		{
			if(s.find(word,pos)!=string::npos)
			{
				n1=s.find(word,pos);
//====================================
	
				n1=s.find(word,pos);
				n1=s.rfind("\n",n1);
				n2=s.find("\n",n1+1);
				s1=s.substr(n1,n2-n1-1);//-1——>去掉\n
	
//====================================	
				cout<<s1<<endl;//文件的结尾是\r\n
				pos=n2;
			}
			else break;
		}
	}
	~test()
	{
		f.close();
	}
};
int main()
{
	test t;
	t.dict();
	return 0;
}
(4)数据库的结构
  • 所有字段都是字符串类型

  • 只使用一个字段:

    -学号1-姓名1-学号2-姓名2-学号3-姓名3-

(5)数据库的两种形式
  • 文本文件
  • 二进制文件
3、类的继承与派生 √√√(test16-5)
(1)单线条公有继承
(2)父类有重载的构造函数
(3)子类对象的使用
(4)虚函数
//类的继承与派生、虚函数 
#include <iostream>
using namespace std;

class point
{
protected:
	int x1,y1;
public:
	point()
	{
		x1=0;y1=0;
	}
	point(int xx1,int yy1)
	{
		x1=xx1;y1=yy1;
	}
	virtual void display()
	{
		cout<<"x1="<<x1<<endl;
		cout<<"y1="<<y1<<endl;
	}
};

class line:public point//public:公有继承	private:私有继承
{
protected:
	int x2,y2;
public:
	line()
	{
//====================================
	
		x2=0;y2=0;
	
//====================================
	}
	line(/**/int xx1,int yy1,int xx2,int yy2/**/)/**/:point(xx1,yy1)/**/
	{
//====================================
		
		x2=xx2;
		y2=yy2;
		
//====================================
	}
	virtual void display()//虚函数:使用基类的指针调用子类的函数
	{
		cout<<"x2="<<x2<<endl;
		cout<<"y2="<<y2<<endl;
	}
};

int main()
{
	point* p;
	line L1(2,3,4,5);
	p=&L1;
	p->display();
	return 0;
}
4、运算符重载 √(test16-6)
(1)输入/输出

​ 类外实现,放在类内:

     friend ofstream& operator<<(ofstream& os,test t)
     friend ifstream& operator>>(ifstream& os,test& t)
(2)双目

​ 类内实现。

(3)单目

​ ++与–要重载两个函数。

//运算符重载
// +,两个线段的顶点坐标分别相加 
// >,返回长度较长的线段
#include <iostream>

//	#include <fstream>

using namespace std;

class point
{
protected:
	int x1,y1;
public:
	point()
	{
		x1=0;y1=0;
	}
	point(int xx1,int yy1)
	{
		x1=xx1;y1=yy1;
	}
	void display()
	{
		cout<<"x1="<<x1<<endl;
		cout<<"y1="<<y1<<endl;
	}
};

class line:public point
{
protected:
	int x2,y2;
public:
	line()
	{
		x2=0;y2=0;
	}
	line(int xx1,int yy1,int xx2,int yy2):point(xx1,yy1)
	{
		x2=xx2;y2=yy2;
	}
	void display()
	{
		cout<<"x1="<<x1<<endl;
		cout<<"y1="<<y1<<endl;
		cout<<"x2="<<x2<<endl;
		cout<<"y2="<<y2<<endl;
	}
	friend ostream& operator<<(ostream& os,line a)
	{
		a.display();
		return os;
	}
	line operator +(line L2)//坐标相加 
	{
//====================================
	
		line t;//这么写前面必须有不带参数的构造函数,不然不然不能这么写,必须初始化
		t.x1=x1+L2.x1;
		t.y1=y1+L2.y1;
		t.x2=x2+L2.x2;
		t.y2=y2+L2.y2;
		return t;
		
//====================================
	}
	bool operator >(line L2)//线段长度 
	{
		bool temp;
		if(((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1))
			>((L2.x2-L2.x1)*(L2.x2-L2.x1)+(L2.y2-L2.y1)*(L2.y2-L2.y1)))
		{
			temp=true;
		}
		else
		{
			temp=false;
		}
		return temp;
	}
};

int main()
{
	//fstream f;
	line L1(1,2,3,4);
	line L2(5,6,7,8);
	if(L1>L2)
	{
		cout<<L1;
	}
	else
	{
		cout<<L2;
	}
	return 0;
}
5、模板 √(test16-7)
(1)类型判断
     if(typeid(T)==tyoeid(int))
(2)注意事项

​ 函数参数的类型与类型参数一致!

 	 int str2num(T s)//不要用(string s)
     {}
//=======类模板========
#include <iostream>
#include <typeinfo>
#include <string>
#include <sstream>
using namespace std;

template <class T>//模板语句
class test
{
private:
	T a,b;
public:
	test(T aa,T bb)
	{
		a=aa;
		b=bb;
	}
	//注意:参数不能是(string s),要与a、b的类型T匹配 
	int str2num(/**/T s/**/)
	{
//====================================
	
		int temp;
		stringstream ss;
		ss<<s;
		ss>>temp;
		return temp;
	
//====================================
	}
	void add()
	{
		if(typeid(T)==typeid(int))//判断模板参数的类型
		{
			cout<<a+b<<endl;
		}
		if(typeid(T)==typeid(string))//判断模板参数的类型
		{
			cout<<str2num(a)+str2num(b)<<endl;
		}
	}
};

int main()
{
	test <int> t1(1,2);//注意:多了"<int>"一节,是类型实参
	t1.add();
	//==================
	test <string> t2("10","20");//注意:多了"<int>"一节,是类型实参
	t2.add();
	return 0;
}
6、计时器与多线程 √ √ √ (test16-8)
(1)键盘(鼠标)响应
     if(GetAsyncKeyState(VK_LEFT))
(2)计时器
     clock()
(3)多线程的实现方法
     if(clock-t>tt)
     {   
         …
         t=clock();
     }
//键盘控制小球运动方向:双线程 
#include <windows.h>
#include <iostream>
#include <ctime>
#include <cstdlib>
#include <conio.h>
using namespace std;

class test
{
private:
	clock_t t1,t2;
	int x,y;
	char c;
public:
	test()
	{
		x=10;y=10;
		t1=clock();
		t2=clock();
		c=0;	//初始化
	}
	void gotoxy(int x,int y)
	{
		HANDLE h;//句柄,对象的索引
		COORD c;//结构体,坐标值
		c.X=x;
		c.Y=y;
		h=GetStdHandle(STD_OUTPUT_HANDLE);
		SetConsoleCursorPosition(h,c);
	}
	void draw()
	{
		gotoxy(x,y);
		cout<<"●";
	}
	void erase()
	{
		gotoxy(x,y);
		cout<<"  ";
	}
	void move1()//ball
	{
		if(clock()-t1>50)	//判断计时器倒没到时间
		{
				//先擦后画
			erase();	//先擦
			switch(c)
			{
				case 1:--y;if(y<0)y=24;break;
				case 2:++y;if(y>24)y=0;break;
				case 3:--x;if(x<0)x=78;break;
				case 4:++x;if(x>78)x=0;break;
			}
			draw();	//后画
			t1=clock();	//计时器重新归零
		}
	}
	void move2()//key
	{
		if(clock()-t2>50)
		{
//====================================

			if(GetAsyncKeyState(VK_ESCAPE))exit(0);
			if(GetAsyncKeyState(VK_UP))c=1;	
			if(GetAsyncKeyState(VK_DOWN))c=2;	
			if(GetAsyncKeyState(VK_LEFT))c=3;	
			if(GetAsyncKeyState(VK_RIGHT))c=4;	
			
//====================================
		}
	}
	void move()
	{
		while(true)
		{
			move1();
			move2();
		}
	}
};

int main()
{
	test t;
	t.move();
	return 0;
}

(二)补充内容

1、多态性的归纳
(1)静态多态性

​ 重载、模板。

(2)动态多态性

​ 虚函数。

2、虚基类(抽象类)

只有(虚)函数声明,没有函数具体实现的类,主要用作类的框架,派生出具体的子类。

3、常类型
(1)常变量(001.cpp)

​ 用于替代C的常量。

//------------------------------------------------
//编号:001
//功能:常变量
//------------------------------------------------
#include <iostream>
using namespace std;
//#define pi 3.1415926//定义常量
const double pi=3.1415926;//定义常变量

int main()
{
	int r=10;
	cout<<"area="<<pi*r*r<<endl;
	return 0;
}
(2)常引用(常变量指针)(002.cpp)

​ 问题:为什么使用引用?

//------------------------------------------------
//编号:002
//功能:常引用(常变量指针) 
//------------------------------------------------
#include <iostream>
using namespace std;

void add(const int& p)
{
	p=p+100; //不可用,因为是常引用
}

int main()
{
	int a=10;
	add(a);
	cout<<a<<endl;
	return 0;
}
4、静类型
(1)静态成员数据 (003.cpp)

​ 类内定义、类外赋值。

//------------------------------------------------
//编号:003
//功能:静态成员数据
//------------------------------------------------
#include <iostream>
using namespace std;

class test
{
private:
	static int a;
	int b;
public:
	test(int bb)
	{
		b=bb;
	}
	void output()
	{
		cout<<"a="<<a<<endl;
		cout<<"b="<<b<<endl;
	}
};
int test::a=10;	//静态成员变量只能在类外面赋值,在类里面复制会报错

int main()
{
	test a1(20),a2(30);
	a1.output();
	a2.output();
	return 0;
}
(2)静态成员函数(004.cpp)

​ 只能使用静态成员数据。

//------------------------------------------------
//编号:004
//功能:静态成员函数
//------------------------------------------------
#include <iostream>
using namespace std;

class test
{
private:
	static int a;
	int b;
public:
	test(int bb)
	{
		b=bb;
	}
	static int square()//静态函数只能使用静态的数据
	{
		return a*a;
	}
	void output()
	{
		cout<<"a="<<a<<endl;
		cout<<"b="<<b<<endl;
	}
};
int test::a=10;//静态的数据只能在类外赋值

int main()
{
	test a1(20);
	cout<<a1.square()<<endl;
	cout<<test::square()<<endl;
	return 0;
}

两点说明:
(1)静态成员属于类,不属于对象,没有this指针;
(2)静态成员可以通过类名或对象访问(见004例)。

(三)模拟练习

1、给出程序的运算结果

读程序,给出运行结果。

2、程序改错

程序中只有一处错误,找到并改正之。

3、程序填空

在程序的空白处填写一条语句,使程序完整。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值