第06章 CORE C++_异常_I/O

1.问题:怎么处理异常?
思路:计算机中具体过程如何???
代码:#include  
using namespace std;  
class Parent{};  
class Child:public Parent{};  
int func()  
{  
try{  
cout<<"good"<throw 250;  
throw"如来神掌";//char const *  
cout<<"morning"<}  
catch(const Child& e){  
//------ 
}  
catch(const Parent& e){  
//------ 
}  
catch(const char* e){// 严格类型匹配//const 和char可以交换顺序 
cout<<" 用九阳神功化解"<}  
catch(long e){  
cout<<"long"<}  
// catch(...){  
// cout<<"unknown exception"<// }  
cout<<"bye"<}//相当于throw的数据全部忽略。 
int main()  
{  
try{  
func();  
}catch(...){  
cout<<" 未知异常"<}  
cout<<"main"<}  
解释:异常这部分是对前面所有知识的异常部分的总结综合!
1.异常:exception,是一个头文件的名字,也是一个类名,只有一个虚函数string what(); 表示对异常

行为的描述。 
不常发生但又不可避免的情况叫做发生异常,但异常不是错误bug。  
处理异常3步走:
1.1.try{可能出现异常的代码或者是调用可能出现异常的函数} 
1.2.if(发现异常情况)  
throw 数据;数据可以为任何类型 
1.3.catch( 类型1 变量名){ 捕获异常数据 
}catch( 类型2 变量名){}******  
集中处理不常见的情况 
catch(...){可以捕获任何异常的数据,但不知道如何发生异常了}
2.异常声明 
标准异常exception what()  
catch(const exception& e){  
cout<2.问题:拷贝构造函数例程
思路:有默认的,逐个成员复制 
class A{  
char* p;  
public:  
A(const char* s){  
p=new char[strlen(s)+1];// 加1是给‘/0’  
strcpy(p,s);// 才是复制字符串,而不是p=s;只有C++风格的string 时才能用赋值表示。 
}
代码:#include  
using namespace std;  
class Name{  
char* p;  
public:  
Name(const char* s){  
p=new char[strlen(s)+1];  
strcpy(p,s);  
}  
~Name(){  
cout<<"free"<<(void*)p<delete[] p;  
}  
Name(const Name& n){// 拷贝函数//避免空间重复释放 
p=new char[strlen(n.p)+1];  
strcpy(p,n.p);  
}  
};  
int main()  
{  
Name n1("chenzq");  
Name n2(n1);//拷贝构造函数调用  
}  
解释:拷贝构造函数:对以前有何补充??? 
1.A a1; A a2(a1); 或A a2=a1;( 变量初始化,不是赋值)  
2. 形参非引用,实参是对象 
3. 返回类型非引用,return 对象;     
如果类中有指针成员指向动态内存,这时候才需要对指针特别对待。
3.问题:用1/2+1/3=5/6,解释运算符重载
思路1:在前程序基础上,用运算符重载cout代替output函数;
在这个程序基础上,添加运算符重载+代替add函数;
思路2:把外部函数直接调入类中,并在操作符重载中减少一个形参 
代码1:#include  
using namespace std;  
class F{  
int n;  
int d;  
public:  
F(int n,int d):n(n),d(d){}  
friend F add(const F& f1,const F& f2);  
friend F operator+(const F& f1,const F& f2);//这是在修改部分基础上,再添加代码  
//friend ostream& output(ostream& os,const F& f);  
friend ostream& operator<<(ostream& os,const F& f);  
};  
F add(const F& f1,const F& f2)  
{  
int d=f1.d*f2.d;  
int n=f1.n*f2.d+f1.d*f2.n;  
return F(n,d);  
}
F operator+(const F& f1,const F& f2)//这是在修改部分基础上,再添加代码//遇到+和F类两个对象,

就调用本函数,返回对象  
{  
int d=f1.d*f2.d;  
int n=f1.n*f2.d+f1.d*f2.n;  
return F(n,d);  
}    
/*ostream& output(ostream& os,const F& f)  
{  
os<return os;  
}*/  
ostream& operator<<(ostream& os,const F& f) //这是修改部分,注释代码是原来程序的//遇到cout和

F类对象,就调用本函数 
{  
os<return os;  
}  
int main()  
{  
F f1(1,2),f2(1,3); //这是修改部分,注释代码是原来程序的 
// output(cout,f1)<<"+";  
// cout<//output(cout,f2)<<"=";  
// output(cout,add(f1,f2))<cout<cout<}  
代码2:#include//最终改写简化版  
using namespace std;  
class F{  
int n;  
int d;  
public:  
F(int n=0,int d=1):n(n),d(d){}  
friend ostream& operator<<(ostream& os,const F& f){//为什么必须是友元???  
os<return os;  
}  
F operator+(const F& f)const{  
int n1=n*f.d+d*f.n;  
int d1=d*f.d;  
return F(n1,d1);  
}  
};  
int main()  
{  
F f1(1,2),f2(1,3);  
cout<}   
解释:运算符重载:前一程序是解释友元概念,本程序其改写版(根据添加代码的需要),添加版体现运

算符重载 
自行规定运算符如何处理各个操作数,以及用什么当运算结果  
要求:至少有一个操作数是你自定义类型的 
关键字:operator@  
4.问题:分数分子分母颠倒
思路:实参对象的分子分母换位返回即可
代码:#include  
using namespace std;  
class F{  
int n;  
int d;  
public:  
F(int n=0,int d=1):n(n),d(d){}  
friend ostream& operator<<(ostream& os,const F& f){  
os<return os;  
}  
friend F operator~(const F& f){//换位返回  
return F(f.d,f.n);  
}  
bool operator!(){  
if(n==0) return true;  
else return false;  
}  
};  
int main()  
{  
F f1(2,3);  
cout<<"f1="<cout<<"~f1="<<~f1<cout<<"f1="<if(!f1)  
cout<<"f1 is false"<else  
cout<<"f1 is true"<}  
解释:单目运算符----- 
1.类的基本格式:
class 类名{
数据成员1(可省,就可不执行);
...
数据成员2(可省,就可不执行);
public:
构造函数(不可省,函数体可为空);
...
析构函数(可省,只是看不见,却必执行);
};
5.问题:分数前++和后++,前--后--
思路:无
代码:#include  
using namespace std;  
class F{  
int n;  
int d;  
public:  
F(int n=0,int d=1):n(n),d(d){}  
friend ostream& operator<<(ostream& os,const F& f){  
os<return os;  
}  
F& operator++(){  
n+=d;  
return *this;//*this 表示当前对象,而this只是一个指针 
}  
F operator++(int){//int 称为哑元//旧值不需要开辟新的空间,所以用引用。 
F old(*this); //对象声明。有时和函数很类似哦。。。吼吼 
operator++();  
return old;  
}  
/*//可与友元替换,比较结果如何?一样。为什么改成友元???
F& operator--(){  
n-=d;  
return *this;  
}  
F operator--(int){  
F old(*this);  
operator--();  
return old;  
}  
*/
friend F& operator--(F& f){  
f.n-=f.d;  
return f;  
}  
friend F operator--(F& f,int){//在f4--时用,为什么???  
F old(f);  
--f;//operator--(f);  
return old;  
}  
};  
int main()  
{  
F f1(2,3),f2(2,3);  
cout<<"++f1="<<++f1<cout->保存到f1  
cout<<"f1="<cout<<"f2++="<cout->++ 
cout<<"f2="<F f3(2,3),f4(2,3);  
cout<<"--f3="<<--f3<cout<<"f3="<cout<<"f4--="<cout<<"f4="<}  
解释:无
6.问题:对象类型转换
思路:无
代码:#include  
using namespace std;  
class F{  
int n;  
int d;  
public:  
F(int n=0,int d=1):n(n),d(d){}  
friend ostream& operator<<(ostream& os,const F& f){  
os<return os;  
}  
operator double(){  
return (double)n/d;  
}  
operator bool(){  
return n!=0;  
}  
};  
int main()  
{  
F f1(1,2),f2(1,3);  
cout<<(double)f1<cout<}  
解释:类型转换运算符只能写成成员函数,而不能写成友元函数
7.问题:输入输出两分数
思路:无
代码:#include  
using namespace std;  
class F{  
int n;  
int d;  
public:  
F(int n=0, int d=1):n(n),d(d){}  
friend ostream& operator<<(ostream& os, const F& f){输出时用  
os << f.n << '/' << f.d;  
return os;  
}  
friend istream& operator>>(istream& is, F& f){//输入时用  
char ch;  
is >> f.n >> ch >> f.d;//输入没有‘/’,即输入分数时,要一并输入‘/’号或者其他字符    

 
return is;  
} //输入千万不能加const    
};  
int main()  
{  
F f1, f2;  
cout << "input 2 fractions:";  
cin >> f1 >> f2;  
cout << "f1=" << f1 << endl;  
cout << "f2=" << f2 << endl;  
}  
解释:无
8.问题:赋值运算符函数例程
思路:在有指针函数指向动态内存时,往往时三大函数同时写析构函数、构造函数、拷贝构造函数
代码:#include  
using namespace std;  
#include  
class Name{  
char* p;  
public:  
Name(const char* s){  
p=new char[strlen(s)+1];  
strcpy(p,s);  
}  
~Name(){  
cout<<"free"<<(void*)p<delete[] p;  
}  
Name(const Name& n){//调用下面的函数 
p=NULL;//new char[strlen(n.p)+1];  
operator=(n);//strcpy(p,n.p);  
}  
Name& operator=(const Name& n){//相对于拷贝构造函数例程,添加部分  
if(this!=&n){  
delete[] p;//释放旧空间 
p=new char[strlen(n.p)+1];//开辟新空间 
strcpy(p,n.p);//复制 
}  
return *this;  
}  
};  
int main()  
{  
Name n1("chenzq");  
Name n2(n1);  
n2=n1;//相对于拷贝构造函数例程,添加部分  
}  
解释:1.operator()(){} 函数对象 可以试试;凡是操作数据的运算符都可以重载,不能重载的运算符:

./.*/?:/::/sizeof/#/##;,需要掌握的:=/[]/类型转换 其它随意,要符合运算符本身的运用习惯。 

 
9.问题:输入字符,整型和其他类型分开保存
思路:1.循环:输入n;判断是否有'/n',' ','/t',有则跳出循环;判断是否不等于cin,不等:清除读

取失败,相等,跳出循环
2.保存剩余的字符
代码:#include  
using namespace std;  
int main()  
{  
int n;  
for(;;){  
cout<<"input an integer:";  
cin>>n;  
if(cin.peek()!='/n'&&cin.peek()!=' '&&cin.peek()!='/t'){  
cout<<" 不是个干净的整数"<break; //只能通过这个语句才能跳出,因为是无限循环 
}  
cout<<"n="<if(!cin){ //检查是否有错误
cout<<" 读取失败!"<cin.clear();// 清除读取失败 
cin.ignore(100,'/n');// 代表一次抛掉几个字符,默认是1个 
}  
else  
break;  
}  
char remain[80];  
cin>>remain; //保存剩余的字符 
cout<<"remain="<}
补充:尝试输入100.45,1234safdgsl;dke,1234 safdgsl;dke,abc几个字符 
解释:cin.clear();清除 
cin.ignore(100,'/n');抛掉多少个字符 
cin.peek();只看不取走,返回ACSII码 
cin.get();看并且取走 
10.问题:输出指定文件内容,统计字符数
思路:根据文件名变成数据流->循环:对每个字符存储到变量中,输出,直到数据流输出完(用while也

可)->关闭文件
代码:#include  
using namespace std;  
#include// 头文件别忘了 
int main()  
{  
char name[100];  
cout<<"input filename:";  
cin>>name;  
ifstream fin(name);//1.相当于:ifstream fin;fin.open("文件名");//不常用的//ifstream是input

file stream缩写  
if(!fin){// 相当于if(!fin.is_open())// 检测函数是否打开 
cerr<<"not exist"<return -1;// 结果不太正确,习惯带-1;  
}  
char ch;
int cnt=0;    
for(;;){//不断执行2.和3.步骤  
ch=fin.get();//2.获得字符并存储  
//cout<<(char)fin.get();//fin>> 会跳过某些字符 
if(!fin){//4.相当于if(!fin.eof())// 检测是否超越了末尾 
fin.clear();//fin是file input的缩写  
break;  
}  
cout<++cnt;  
}  
fin.close();// 关闭后释放资源 
cout<<"length:"<}  
解释:I/O :input/output stream 流:一系列字符(字节) 
流:有序的一次传递,可以缓冲、重定向 
cin/cout 可以缓冲也可以重定向 
/cerr :不缓冲不重定向 
/clog:可以缓冲不重定向 
类型:cin istream  
cout/cerr/clong ostream    
11.问题:输入指定文件,输出结果,并输出结果到指定文件
思路:输入指定文件->判断是否合法->功能->关闭输入文件->输出变量->创建输出文件->判断能否创建->

输出变量->关闭输出文件
代码:#include  
using namespace std;  
#include// 头文件别忘了 
int main()  
{  
int sum=0;  
int one;  
int cnt=0;  
//ifstream fin("data"); //可与下4行替换 
char name[100];  
cout<<"input filename:";  
cin>>name;  
ifstream fin(name);
if(!fin){  
cout<<" 找不到文件data"< return -1;  
 }  
 while(fin>>one){//结果还是fin//核心功能???  
 sum+=one;  
 ++cnt;  
 }  
 fin.clear();  
 fin.close();  
 cout<<"sum="< cout<<"cnt="< ofstream fout("result");//创建文件result  
 if(!fout){  
 cout<<" 无法创建文件result"< return -1;  
 }  
 fout<<" 总和为"< fout<<" 数据个数为"< fout<<" 平均值是"<<(double)sum/cnt< fout.close();  
}   
解释:无
12.问题:输入字符,将其输出
思路:cin.get(变量):1.只能接受输入5个字符,并输出
2.for结构可以接受无数个字符,并输出
3.while结构可以接受无数个字符,并输出
代码1:#include  
using namespace std;  
int main()  
{  
char a,b,c,d,e;  
cout<<"input some chars:";  
cin.get(a).get(b);  
cin.get(c);  
cin.get(d);  
cin.get(e);  
cout<<"a=["<cout<<"b=["<cout<<"c=["<cout<<"d=["<cout<<"e=["<}  
代码2:#include  
using namespace std;  
int main()  
{  
char ch;  
cout<<"input some text:/n";  
for(;;){//可替换为while结构  
cin.get(ch);  
if(cin){//合法  
cout<if(ch=='/n') //换行(即回车) 
break;//跳出无线循环  
}  
}  
}
代码3:#include  
using namespace std;  
int main()  
{  
 char ch;  
 cout<<"input some text:/n";  
 while(cin.get(ch)){//可替换为for结构  
 cout.put(ch);// cout< if(ch=='/n')  
 break;  
 }  
}   
解释:>> 格式化输入 键盘上输入的是个字符序列  而到程序中就成各种类型变量 
会自动处理格式的问题 cin>>各种类型变量  
char ch; in.get(ch) 带回的就是当前对象  
13.问题:编译后,输入“./a.out 文件名”,可得到带行标识的输出的指定文件内容
思路:1.判断命令是否有参数,若无不执行2和3步
2.读入文件名是否合法,如不合法,不执行3步
3.逐行读入文件内容,并输出,直到结束
4.关闭文件
代码:#include  
using namespace std;  
#include //文件类型头文件 
#include//字符串类型头文件  
int main(int argc,char* argv[])//类似于命令行的输入  
{  
if(argc!=2){//如果命令后无参数  
cout<<*argv<<"filename"<return 0;//也有跳出功能???  
}  
ifstream fin(argv[1]);//输入argv[1]内容  
if(!fin){//如果不合法  
cout<<"error!"<return -1;//也有跳出功能???  
}  
string line;  
for(int i=1; ;i++){  
getline(fin,line);//string头文件包含函数;按行从文件输入  
if(!fin) break;//文件结束跳出  
cout<}  
fin.close();  
}
补充:尝试执行:wc 文件名 
解释:in.getline( 字符数组名,数组大小)有长度限制 
getline(in,string 对象)没有长度的限制,一般用此用法 
14.问题:读取passwd文件,逐项内容输出 
思路:前面的基础上,分别读取,存储和输出
代码:#include  
using namespace std;  
#include  
#include  
int main()  
{   
 string name,pass,id,gid,comm,dir,shell;  
 ifstream fin("/etc/passwd");  
 if(!fin){
cout<<"I can't open it."< return -1;  
}   
for(int i=0;i<5;i++){// 读取5行 
getline(fin,name,':');//识别':',此为判断标志读取内容  
getline(fin,pass,':');//识别':',此为判断标志读取内容  
 getline(fin,id,':'); //识别':',此为判断标志读取内容 
 getline(fin,gid,':'); //识别':',此为判断标志读取内容 
 getline(fin,comm,':');  //识别':',此为判断标志读取内容
 getline(fin,dir,':');//识别':',此为判断标志读取内容  
 getline(fin,shell,':'); //识别':',此为判断标志读取内容 
 cout<<"name:/t"< cout<<"pass:/t"< cout<<"id:/t"< cout<<"gid:/t"< cout<<"comm:/t"< cout<<"dir:/t"< cout<<"shell:/t"<}   
 fin.close();  
}   
解释:在某些UNIX系统中,如果没有相应权限,将不能打开passwd文件,即本程序不能完全执行。
15.问题:输入大于9的数值,输出
思路:
代码:#include  
using namespace std;  
int main()  
{  
int n;  
char ch;//应该是单个字符???  
cout<<"input an integer(num>9):";  
cin.get(ch); //为什么大费周章的用这个函数???
if(ch>='0'&&ch<='9')//如果注释这部分会是什么样的结果?  
cin.putback(ch);    
cin>>n;  
cout<<"n="<}  
解释:cin.peek()  
cin.putback( 字符)退回的字符必须是刚才用get读走的字符 
putback 不与peek一起用 
16.问题:保存数的参数到文件,再从文件根据参数,输出数
思路1:读入一个整型数,存到sav文件中其内存地址和字节数
思路2:读入sav文件内容,输出指定数
代码1:#include  
#include  
using namespace std;  
int main()  
{  
int n=123456789;  
ofstream fout("sav");// 写到sav 文件中 
if(!fout){  
cout<<"error!"<return -1;  
}  
fout.write((char*)&n,sizeof(n));//write 总是带个类型转换 
fout.close();  
}  
代码2:#include  
#include  
using namespace std;  
int main()  
{  
int n=0;  
ifstream fin("sav");  
if(!fin){  
cout<<"file sav not found"<return -1;  
}  
fin.read((char*)&n,sizeof(n));  
fin.close();  
cout<<"n="<}   
解释:fin.read(内存地址,字节数) 一般用来读文件。从文件中把数据读逐个字节读出来,原样逐字节

放到指定地址开始的内存中 
fout.write( 内存地址,字节数) 把从指定内存地址开始指定字节数的内存数据逐字节写到文件中 
read和write 根本不关心数据类型,只关心在内存里几个字节数
17.问题:用命令格式复制文件内容到指定文件
思路:判断读入文件和输出文件是否合法->拷贝文件内容到指定文件->关闭读入和输出文件
代码:#include  
#include  
using namespace std;  
int main(int argc,char* argv[])  
{  
if(argc!=3){  
cout<<*argv<<"oldfile newfile"<return 0;  
 }  
 ifstream fin(argv[1]);  
 ofstream fout(argv[2]);  
 if(!fin||!fout){  
 cout<<"error!"< return -1;  
 }  
 char buf[1000];  
 do{//该部分为核心功能语句  
 fin.read(buf,1000); //每次读取1000个单元 
 fout.write(buf,fin.gcount());  
 }while(fin);  
 fin.clear();  
 fin.close();  
 fout.close();  
}   
解释:别把指针保存到文件中,它会有潜在的危险 
fin.gcount() 表示这次read 读取到了多少字符 
18.问题:拷贝字符串
思路:判断创建输出文件->输入字符串->输出到指定文件->关闭输出文件
代码:#include  
#include  
#include  
using namespace std;  
int main()  
{   
 ofstream fout("log.txt",ios::app);//ios::app 表示追加,否则就默认清空原有的内容。 
if(!fout){  
 cout<<"error!"< return -1;  
}   
cout<<"input some text:";  
string str;  
 getline(cin,str);  
 fout< fout.close();  
}   
解释:上一程序可以此拷贝方法重写
19.问题:不能编译???
思路:
代码:#include  
#include  
#include  
using namespace std;  
int main()  
{  
fstream fio("file");//为什么调不出fstream???  
if(!fio){  
cout<<"error!"<return -1;  
}  
char buf[10];  
fio.read(buf,10);  
cout.write(buf,10);  
cout<for(int i=0;i<5;i++)  
swap(buf[i],buf[i+5]);  
fio.seekp(0);// 指定往哪写 
fio.write(buf,10);  
fio.seekg(10);// 表示到哪里去读,0表示第一个位置 
char ch;  
fio.get(ch);  
cout<<"ch="<fio.close();  
}  
解释:如果又要读要写,就用fstream
20.问题:指定宽度输出内容
思路:指定宽度,指定fill;不指定宽度;指定宽度,不写指定fill;
代码:#include  
using namespace std;  
int main()  
{  
cout.width(10);  
cout.fill('*'); //为什么是7个??? 
cout<<123<cout<<123<cout.width(20);  
cout<<123<}  
解释:width() 指定宽度是一次性的,如果指定的宽度的小于输入的宽度,输出时是需要的宽度不会截取 
21.问题:指定小数位
思路:指定位数并四舍五入,指定小数后位数,指定数总位数
代码:#include  
using namespace std;  
int main()  
{  
double d=12345.6789;  
cout<<"d="<cout.precision(8);  
 cout<<"d="< cout.precision(3);  
 cout.setf(ios::fixed);//表示小数点后的位数 
 cout<<"d="< cout.unsetf(ios::fixed);//表示数字总位数 
 cout<<"d="<}   
解释:指定小数的精度:precision()
22.问题:数值不同表示
思路:无
代码1:#include  
using namespace std;  
int main()  
{  
cout<cout<cout<}  
代码2:#include  
using namespace std;  
#include  
int main()  
{  
cout<cout<}   
解释:输出控制标志和控制符

 

你可能感兴趣的:(第06章 CORE C++_异常_I/O)