C++复习指南
1.类的使用
主要注意需要传参的时候定义为
test t(10,20);
不需要传参的时候定义为
test t;
private是完全私有的,只有自己可以访问,派生类和外部都不可以访问
protected是受保护的,只有派生类可以访问,外部不能访问
在类外如果想使用类中的成员,只能直接使用public类型的,proteced和private都是不能访问的,对于类外而言,这两个是完全相同的。
所以类外t.a=5是错误的,正确写法是调类内函数修改
//了解,考试不考文字
函数重载(往年未考,不做重点)
#include<bits/stdc++.h>
using namespace std;
int add(int a,int b)
{
return a+b;
}
int add(int a,int b,int c)
{
return a+b+c;
}
int add(double a,double b )
{
return a+b;
}
int main ()
{
int a=1,b=2,c=3;
cout<<"a+b="<<add(a,b)<<endl;//通过参数个数或者类型来辨别
cout<<"a+b+c="<<add(a,b,c)<<endl;
cout<<"小数"<<add(1.5,6.3)<<endl;
}
//a+b=3
//a+b+c=6
//小数7
//这里根据参数类型自动选择函数
类内构造函数重载
#include<bits/stdc++.h>
using namespace std;
class ss
{
private:
int a,b;
public:
ss()
{
a=10;
b=20;
}
ss(int aa,int bb)
{
a=aa;
b=bb;
}
ss(int bb)
{
a=10;
b=bb;
}
int add()
{
return a+b;
}
} ;
int main ()
{
ss a1,a2(15,25),a3(50);
cout<<"a+b="<<a1.add()<<endl;
cout<<"a+b="<<a2.add()<<endl;
cout<<"a+b="<<a3.add()<<endl;
}
//a+b=30
//a+b=40
//a+b=60
引用
#include<bits/stdc++.h>
using namespace std;
void Swap(int* x,int* y)
{
int p;
p=*x;
*x=*y;
*y=p;
}
void Swap(int& x,int& y )
{
int p;
p=x;
x=y;
y=p;
}
int main ()
{
int a=1000,b=2000;
Swap(&a,&b);
cout<<a<<endl;
cout<<b<<endl;
Swap(a,b);
cout<<a<<endl;
cout<<b<<endl;
}
//2000
//1000
//1000
//2000
//这里用了函数重载,C++引用以后就相当于变量本身
void add(vector<int>& b,int n) //vector记得引用
{
for(int i=0;i<b.size();i++)
{
cout<<b[i]<<endl;
}
}
add(a,100); //传参和数组一样传名就行,区别是数组b是首地址,cout<<a会报错
类的继承和派生(重点)
#include <iostream>
using namespace std;
class point_2d
{
protected:
int x,y;
public:
point_2d()
{
x=0;
y=0;
}
point_2d(int xx,int yy)
{
x=xx;
y=yy;
}
void output()
{
cout<<"x="<<x<<endl;
cout<<"y="<<y<<endl;
}
};
class point_3d:public point_2d //继承这句别拼错了
{
protected:
int z;
public:
point_3d()//: point_2d()其实有一句这个,可以省略
{
z=0;
}
point_3d(int xx,int yy,int zz): point_2d(xx,yy) //考点,这句得抄
{
z=zz;
}
void output()
{
cout<<"x="<<x<<endl;
cout<<"y="<<y<<endl;
cout<<"z="<<z<<endl;
}
};
int main()
{
point_3d b1,b2(1,2,3);
b1.output();
b2.output();
return 0;
}
//x=0
//y=0
//z=0
//x=1
//y=2
//z=3
//这里注意因为是定义的子类对象,所以调用子类函数,个人见解 子类=子类+父类(父类和子类的东西子类都能用)
class test1
{
protected:
int x1,x2,y1,y2;
};
class test2:public test1
{
test2(int xx1,int xx2,int yy1,int yy2):test1(xx1,xx2,yy1,yy2)
{
//test2里面不需要定义点
}
};
//举个小例子强化一下这句
#include <iostream>
#include <stdlib.h>
using namespace std;
class A
{
public:
void f1()
{
cout<<1;
}
};
class B:public A
{
public:
void f1()
{
cout<<2;
}
};
int main( )
{
class B t;
t.A::f1();
return 0;
}
//同名函数
类的组合调用
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
class birthday
{
protected:
int year,month,day;
public:
birthday(int yy,int mm,int dd)
{
year=yy;
month=mm;
day=dd;
}
void output()
{
cout<<year<<'-'<<month<<'-'<<day<<endl;
}
};
class student
{
protected:
string name;
birthday a;//定义一个对象
public:
student(string nn,int xx,int yy,int zz): a(xx,yy,zz)//像对父类一样给其赋值
{
name=nn;
}
void output()
{
cout<<"name:"<<name<<endl;
a.output();
}
};
int main()
{
student a("hwq",2000,9,26);
a.output();
system("pause");
return 0;
}
//这里一个类内可以定义另一个内,参数像继承一样一起传
2.运算符重载(重点)
//输入输出运算符重载,可内可外,内要加friend
class test
{
};
//返回值类型 关键字 重载运算符 (参数表,os自己起的返回参数名,类的对象)
ostream& operator <<(ostream& os,test a)
{
a.output();//重载的内容
return os;//返回值
}
istream& operator >>(istream& os,test& a)
{
a.input();
return os;
}
//双目运算符重载
class test
{
//友元 返回值类型 关键字 重载运算符 (参数表,os自己起的返回参数名,类的对象)
friend ostream& operator <<(ostream& os,test a)
{
a.output();//重载的内容
return os;//返回值
}
friend istream& operator >>(istream& os,test& a)
{
a.input();
return os;
}
//返回值类型 关键字 重载运算符 参数表
test operator +(test a)
{
test temp;
temp.m=m+a.m;
temp.n=n+a.n;
return temp;
}
};
//这里必须放类内,因为双目涉及两个类,省略的那个即当前类,出现在参数里的是+右边的类(这里是a)
//另外注意双目返回的对象是类,所以要定义个temp
//单目运算符重载
class test
{
test operator ++()//++i
{
test temp(i);
temp.i=i+1;
i++;
return temp;
}
test operator ++(int n)//规定带参数的是i++
{
test temp(i);
temp.i=i;
i++;
return temp;
}
};
//注意在类内,i是当前类内参数
//单目运算符重载,顺序别反
class test
{
test operator --()//--i
{
--i;
return *this;
}
test operator --(int n)//规定带参数的是i++
{
test temp(*this);
--(*this);
return temp; //表达式的值不变
}
};
//注意在类内,i是当前类内参数
3.容器(重点)
复习课16-4 test很好
vector
//vector使用
bool sort1(int n1, int n2)
{
return n1>n2;//n1>n2即前大后小,反向排序
}
int main()
{
vector<int> a;
a.push_back(1);
a.push_back(2);
a.push_back(3);
a.erase(a.begin()+1);//擦掉了2,这里begin()+1的位置可以换成迭代器
a.insert(a.begin()+1,4);//在原来2的位置上插入了4
//反向排序
int sum=accumulate(a.begin(),a.end(),0);//从0开始累加求和
int max=*max_element(a.begin(),a.end());//求最大
int min=*min_element(a.begin(),a.end());//求最小
sort(a.begin(),a.end(),sort1);
}
list
//list使用
list<int> a;
a.push_back
a.erase
a.insert
和vector函数用法差不多,这里重点讲下区别
list是双向链表,必须配合迭代器p使用,初始化p=a.begin(),然后写循环让p++一步步挪,不能跳跃挪
cout<<a[0]; //错,但vector可以
a.erase(a.begin()+1); //错
int main()
{
list<int> a;
a.push_back(1);
a.push_back(2);
list<int>::iterator p;
p=a.begin();
p++;
cout<<*p;
return 0;
}//结果是2
//示例程序 将grade=4的学生删掉并按成绩由大到小排序
#include <iostream>
#include <ctime>
#include <list>
#include<bits/stdc++.h>
using namespace std;
struct S
{
int number;
int score;
int grade;
};
class test
{
private:
int n,i;
list<S>a;
list<S>::iterator p;
public:
test(int nn)
{
n=nn;
S temp;
srand(time(0));
for(i=0;i<n;++i)
{
temp.number=i+1;
temp.score=rand()%101;
temp.grade=rand()%4+1;
a.push_back(temp);
}
}
void browse()
{
cout<<"===================="<<endl;
for(p=a.begin();p!=a.end();++p)
{
cout<<p->number<<"-"<<p->score<<"-"<<p->grade<<endl;
}
}
static bool sort1(S s1,S s2)
{
return s1.score>s2.score;//成绩由大到小
}
void Sort()
{
a.sort(sort1);//list调用sort和别的不一样
}
static bool find1(S s)
{
return s.grade==4;
}
void Find()
{
p=a.begin(); //注意每次在新函数使用迭代器位置要归零
while(1)
{
p=find_if(p,a.end(),find1);
if(p!=a.end())
{
p=a.erase(p);//和vector不一样,删除的时候要给p赋值,返回值为p的上一个位置,不然指针会飞掉
}
else break;
}
}
void Erase(int m)
{
int i=0;
p=a.begin();
for(i=0;i<m;i++)
{
++p;//list是双向链表,不支持直接+m,只能一点点挪,m是挪移位置
//p=a.begin()+m;
}
a.erase(p);
}
void Insert()
{
S temp;
temp.number=12345;
temp.score=12345;
temp.grade=12345;
p=a.begin(); //list插入快
a.insert(p,temp);//在p之前插入12345
}
};
int main()
{
test t(8);
t.browse();
t.Find();
t.Sort();
//t.Insert();
//t.Erase(1);
t.browse();
return 0;
}
//主要注意erase和sort和vector区别
map(年年考,重点)
map是(键-值)对的集合,数据结构是二叉树,会自动按照键值排序,存储顺序与输入顺序无关
map<int,string> a;
map<int,string>::iterator p;
a.insert(pair<int,string>(1,"zz"));
a.insert(pair<int,string>(2,"yy"));
p=a.find(1);
p++;
cout<<p->first<<p->second;
a.erase(p);
//输出2yy,然后擦除2yy
示例程序
//map二叉树
#include <iostream>
#include <vector>
#include <algorithm>
#include <ctime>
#include <cstdlib>
#include<list>
#include<map>
using namespace std;
struct S
{
int number;
int score;
};
class test
{
private:
int n,i;
map<int,S>a;//左边的int叫键值,键值动不了,动右边
map<int,S>::iterator p;
public:
test(int nn)
{
n=nn;
S temp;
srand(time(0));
for(i=0;i<n;++i)
{
temp.number=rand()%10;
temp.score=rand();
a.insert(pair<int,S>(i+1,temp));//指定键值从1开始往后排
}
}
void browse()
{
cout<<"===================="<<endl;
for(p=a.begin();p!=a.end();++p)
{
cout<<p->first<<":"<<p->second.number<<"---"<<p->second.score<<endl;
}
//因为是数对,所以第一个是first,第二个是second
}
void Find() //不需要变,只能检索键值
{
int temp;
cout<<"input:";
cin>>temp;
p=a.find(temp);//map只查键值,查别的不用map
if (p!=a.end())
{
cout<<p->first<<":"<<p->second.number<<"---"<<p->second.score<<endl;
}
else cout<<"no find!"<<endl;
}
void Erase(int m)
{
int i=0;
p=a.begin();
for(i=0;i<m;i++)
{
++p;
}
//p=a.begin()+m; //这是错的,map只能一点点挪,map树状结构存储不连续,加法无意义
a.erase(p);
}
void Insert()
{
S temp;
temp.number=12345;
temp.score=12345;
p=a.begin();
a.insert(pair<int,S>(100,temp));//指定键值
}
};
int main()
{
test t(10);
//t.browse(); //左边键值会自动由小到大排好,插入时二叉树特点
t.Insert();
t.Erase(9);
t.browse();
//t.Find();
return 0;
}
16-4 test
//容器:遍历、排序、检索
#include<iostream>
#include<ctime>
#include<cstdlib>
#include<vector>
#include<list>
#include<map>
#include<algorithm>
using namespace std;
struct S
{
int number;
int score;
bool operator==(S s)
{
return number==s.number;
}
};
class test
{
private:
int n,i;
vector<S>a1;
vector<S>::iterator p1;
list<S>a2;
list<S>::iterator p2;
map<int,S>a3;
map<int,S>::iterator p3;
static int m;
public:
test(int nn)
{
n=nn;
srand(time(0));
S temp;
for(i=0;i<n;++i)
{
temp.number=rand();
temp.score=rand();
a1.push_back(temp);
a2.push_back(temp);
a3.insert(pair<int,S>(i+1,temp));
}
}
void browse1()
{
for(p1=a1.begin();p1!=a1.end();++p1)
{
cout<<p1->number<<"-"<<p1->score<<endl;
}
}
void browse2()
{
for(p2=a2.begin();p2!=a2.end();++p2)
{
cout<<p2->number<<"-"<<p2->score<<endl;
}
}
void browse3()
{
for(p3=a3.begin();p3!=a3.end();++p3)
{
cout<<p3->first<<":"<<p3->second.number<<"-"
<<p3->second.score<<endl;
}
}
static bool sort1(S s1,S s2)
{
return s1.number>s2.number;
}
void Sort1()
{
sort(a1.begin(),a1.end(),sort1);
}
void Sort2()
{
a2.sort(sort1);
}
void Find1()
{
S temp;
cout<<"input number:";
cin>>temp.number;
p1=a1.begin();
while(1)
{
p1=find(p1,a1.end(),temp);
if(p1!=a1.end())
{
cout<<p1->number<<"-"<<p1->score<<endl;
++p1;
}
else break;
}
}
void Find2()//与Find1相同
{
S temp;
cout<<"input number:";
cin>>temp.number;
p2=a2.begin();
while(1)
{
p2=find(p2,a2.end(),temp);
if(p2!=a2.end())
{
cout<<p2->number<<"-"<<p2->score<<endl;
++p2;
}
else break;
}
}
void Find3()
{
int temp;
cout<<"input number:";
cin>>temp;
p3=a3.find(temp);
if(p3!=a3.end())
cout<<p3->first<<":"<<p3->second.number<<"-"
<<p3->second.score<<endl;
else cout<<"no find!"<<endl;
}
static bool find1(S s)
{
return s.score<m;
}
void Find_if()
{
cout<<"input score blow:";
cin>>m;
p1=a1.begin();
while(1)
{
p1=find_if(p1,a1.end(),find1);
if(p1!=a1.end())
{
cout<<p1->number<<"-"<<p1->score<<endl;
++p1;
}
else break;
}
}
};
int test::m;
int main()
{
test t(100000);
t.Find_if();
return 0;
}
//不背也行看最底下那个怎么写叭
4.键盘码
ESC 27
ENTER 32
SPACE 32
上箭头 -32 72
左箭头 -32 75
右箭头 -32 77
下箭头 -32 80
//2020春考试卷子 横线那行会了就行
#include <windows.h>
#include <iostream>
#include <ctime>
#include<conio.h>
using namespace std;
class test
{
private:
int x,y;
clock_t t;
public:
test()
{
x=5;
y=5;
t=clock();
}
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 key(clock_t& t,int tt)
{
if(clock()-t>tt)
{
//------------------------------------------
//绘制图形
draw();
//接收键盘码
char c1,c2;
c1=getch();
if(c1==27)
{
exit(0);
}
else if(c1==-32)
{
//清除图形
erase();
//坐标变换
c2=getch();
switch(c2)
{
case 72:
y=(--y+25)%25; //注意减到0以后并不会自动变正数,所以手动变正
break;
case 75:
x=(--x+80)%80;
break;
case 77:
x=(++x)%80;
break;
case 80:
y=(++y)%25;
break;
}
}
//------------------------------------------
}
}
void move()
{
while(true)
{
key(t,10);
}
}
};
int main()
{
test t;
t.move();
return 0;
}
//这里注意小球占俩字符,erase要输出俩空格
计时器
int tt;
clock_t t;
t=clock();
经常会用 if(clock()-t>tt)//当前时间-t
作为判断时间间隔依据
5.字符串
//插入子串s.insert(插入位置,“串”);
//删除子串s.erase(开始位置,长度);
//查找子串s.find(内容,开始位置);
//取子串s.substr(开始位置,长度);
//替换子串s.replace(开始位置,替换长度,替换串);
string::size_type n;//无符号长整数,字符比较短的时候可用int替代
if(s.find(s1,0)!=string::npos)//npos是比字符串长度还大的一个整数,就是代表没查到的意思
模糊查询例子
#include <windows.h>
#include <iostream>
#include<string>
using namespace std;
int main()
{
string s,s1;
s="s-王小二-89-张小三-95-李小二-100";
s1="小二";
string name ,score;
string::size_type n,n1,n2,pos=0;
while(1)
{ if(s.find(s1,pos)!=string::npos)
{
n=s.find(s1,pos);
n=s.rfind("-",n);//逆向查找
n1=s.find("-",n+1);
n2=s.find("-",n1+1);
name=s.substr(n+1,n1-n-1);
score=s.substr(n1+1,n2-n1-1);
cout<<"name:"<<name<<endl;
cout<<"score="<<score<<endl;
pos=n2;
}
else break;
}
return 0;
}
字符串逆序两种方法
void Reverse()
{
string temp;
s="1234";
temp=s;
int len=s.size();
for(int i=0;i<len;++i)
{
s[i]=temp[len-i-1];
}
}
//等价于库函数
//reverse(s.begin(),s.end());
字符串流转换
string int2str(int n)
{
string s;
stringstream ss;
ss<<n;
ss>>s;
return s;
}
int str2int(string s)
{
int n;
stringstream ss;
ss<<s;
ss>>n;
return n;
}
6.文件处理(超重点)
//打开文件的几种方式
ofstream f1("1.dat",ios::binary); //以写形式打开,o是out从程序往外出
f1.write((char*)s.c_str(),s.size());
ifstream f2(filename.c_str(),ios::binary); //以读形式打开,i是in进到程序里
f2.read((char*)s2.c_str(),n);//第一个指针决定从第几位开始读n个字符到s2
getline(f,s);//把当前行读到s里
//不过更建议下面的方式,读写都有了
class test
{
private:
fstream f;
public:
test()
{
f.open("test.doc",ios::in|ios::out|ios::binary);
if(!f)
{
cout<<"file error!"<<endl;
exit(0);
}
else
{
}
}
~test()
{
f.close();
}
};
//ios::beg:表示输入流的开始位置
//ios::cur:表示输入流的当前位置
//ios::end:表示输入流的结束位置
//seek定位指针位置,g是get,读指针;p是put,写指针
f2.seekg(0,ios::end);//0是偏移量,正则向后偏移,负则向前偏移,这里是将读指针移到文件尾
n=f2.tellg();//返回读指针位置
tellp 用于返回写入位置,tellg 则用于返回读取位置
例子见考试卷最后一题
#include <iostream>
#include <map>
#include <string>
#include <fstream>
#include <algorithm>
using namespace std;
class test
{
private:
fstream f;
string::size_type n;
map<string,string>a;
map<string,string>::iterator p;
public:
test()
{
string s,s1;
f.open("英汉词典.txt",ios::in);
while(true)
{
//moon n.月亮
//------------------------------------------
string::size_type n,n1; //换int也能跑
string temp;
getline(f,temp);
n=temp.find(" ",0);
s=temp.substr(0,n);
n1=temp.rfind(" ",temp.size()); //逆序查找
s1=temp.substr(n1+1,temp.size()-n1-1);
a.insert(pair<string,string>(s,s1));
if(f.eof()) //文件指针走到尾退出循环
{
break;
}
//------------------------------------------
}
}
~test()
{
f.close();
}
void FIND()
{
string word;
cout<<"input 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;
}
//这里主要注意getline用法和循环读到文件尾
//将字符'A'写入二进制文件的最后一个字节
#include <iostream>
#include <conio.h>
#include <fstream>
using namespace std;
int main()
{
char c='A';
fstream f("test.txt",ios::in|ios::out|ios::binary);
f.seekp(-1,ios::end); //这里会覆盖文件最后一个字节
f.write((char*)&c,1);
f.close();
return 0;
}
7.拓展
虚函数
//动态多态性:虚函数
#include <iostream>
#include <string>
using namespace std;
class A
{
protected:
int a;
double b;
public:
A(int aa,double bb){a=aa,b=bb;}
virtual double total()
{
return a*b;
}
};
class B:public A
{
protected:
double c;
public:
B(int aa,double bb,double cc):A(aa,bb){c=cc;}
virtual double total()
{
return a*b*c;
}
};
int main()
{
A a(2,10);
B b(3,10,0.2);
A *p;
p=&a;
cout<<p->total()<<endl;
return 0;
}
//自己运行+改改试试输出
//区别是不加virtual默认调用父类函数
类模板
//=======类模板========
#include <iostream>
#include <typeinfo>
#include <string>
#include <sstream>
using namespace std;
template<class T1,class T2>
class test
{
private:
T1 a,b;
public:
test(T1 aa,T1 bb)
{
a=aa;b=bb;
}
T2 T1_T2(T1 s)
{
stringstream ss;
T2 temp;
ss<<s;
ss>>temp;
return temp;
}
T2 add()
{
return T1_T2(a)+T1_T2(b);
}
};
int main()
{
test<string,double> t("1.2","3.4");
cout<<t.add()<<endl;
return 0;
}
//类模板就是自动填充数据类型
键盘操作小球-计时器
//键盘控制小球移动
#include <windows.h>
#include <iostream>
#include <ctime>
#include <iomanip>
#include "basic.cpp" //里面放了些隐藏光标和goto坐标函数
using namespace std;
class test
{
private:
clock_t t1,t2;
int x,y;
int n;
public:
test(int xx,int yy)
{
x=xx;y=yy;
t1=clock();
t2=clock();
hide_cursor();
}
void draw()
{
gotoxy(x,y);
cout<<"●";
}
void erase()
{
gotoxy(x,y);
cout<<" ";
}
void output()//测试gotoxy用函数
{
gotoxy(x,y);
cout<<"hello!";
}
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_LEFT))
{
--x;
if(x<0)x=78;
};
if(GetAsyncKeyState(VK_RIGHT))
{
++x;
if(x>78)x=0;
};
if(GetAsyncKeyState(VK_UP))
{
--y;
if(y<0)y=24;
};
if(GetAsyncKeyState(VK_DOWN))
{
++y;
if(y>24)y=0;
};
draw();
t2=clock();
}
}
void move()
{
while(1)
{
move1();
move2();
}
}
~test()
{
show_cursor();
}
};
int main()
{
test t(10,10);
t.move();
return 0;
}
//键盘码还是背这个叭
8.有用小函数
int a[10];
memset(a,0,sizeof(a));//把里面初始化为0
sqrt(3);//开平方,结果是1.732
return *this返回的是当前对象的克隆或者本身(若返回类型为A, 则是克隆, 若返回类型为A&, 则是本身 )。return this返回当前对象的地址(指向当前对象的指针)
//不用纠结太多,把*this当成当前对象用就行
int *a;
a=new int[n];
delete []a; //这里清理了第二句开辟的空间,而不是清掉了a这个指针
delete a; //效果是一样的,但有可能会引起严重的后果,不安全,所以尽量使用上面的
srand(time(0));
rand();
temp=(rand()*rand())%n //随机性会好一点