Day46、异常、I/O流

来源:互联网 发布:网络公司财务 编辑:程序博客网 时间:2024/06/07 14:23

一、            异常

1、 常见错误

1) 语法错误

2) 逻辑错误

3) 功能错误

4) 设计缺陷

5) 需求不符

6) 环境异常

7) 操作不当

2、 传统的错误处理机制

1) 通过返回值表示错误

优点:安全,函数调用路径中所有局部对象都能被正确的析构,不会出现内存泄露

缺点:错误处理流程比较复杂,逐层判断,代码臃肿

1.cpp

#include<iostream>

#include<cstdio>

using namespace std;

class A{

public:

   A(void){

       cout<<"A::A()"<<endl;

    }

    ~A(void){

       cout<<"A::~A()"<<endl;

    }

};

int func3(void){

    Aa;

   FILE* fp=fopen("none.txt","r");

   if(fp==NULL){

       cout<<"file open error!"<<endl;

       return -1;

    }

   //...操作

   fclose(fp);

   return 0;

}

int func2(void){

    Aa;

   if(-1==func3()){

       return -1;

    }

   //...其他操作

   return 0;

}

int func1(void){

    Aa;

   if(-1==func2()){

       return -1;

    }

   //...其它操作

}

int main(void){

   if(-1==func1()){

       return -1;

    }

   //....

   return 0;

}

tarena@tarena-virtual-machine:~/day47$./a.out

A::A()

A::A()

A::A()

file open error!

A::~A()

A::~A()

A::~A()

2) 通过远眺机制处理错误

优点:不需要逐层判断,一步到位错误处理,代码精炼

缺点:函数调用路径中的局部对象失去被析构的机会,会形成内存泄露

2.cpp

#include<iostream>

#include<cstdio>

#include<csetjmp>

using namespace std;

jmp_buf g_env;

class A{

public:

   A(void){

       cout<<"A::A()"<<endl;

    }

   ~A(void){

       cout<<"A::~A()"<<endl;

    }

};

int func3(void){

    Aa;

   FILE* fp=fopen("none.txt","r");

   if(fp==NULL){

       longjmp(g_env,-1);

    }

   //...操作

   fclose(fp);

   return 0;

}

int func2(void){

    Aa;

   func3();

   //...其他操作

   return 0;

}

int func1(void){

    Aa;

   func2();

   //...其它操作

}

int main(void){

   if(setjmp(g_env)==-1){

       cout<<"file open error!"<<endl;

       return -1;

    }

   func1();

   //....

   return 0;

}

tarena@tarena-virtual-machine:~/day47$./a.out

A::A()

A::A()

A::A()

file open error!

3、 C++异常机制

结合两种错误处理的优点,同时避免它们的缺点,在形式上实现一步到位,同时保证所有局部对象得到正确的析构

4、 C++异常机制

4、1 异常抛出

1)throw异常对象

2)可以抛出基本类型的异常对象

throw -1;

throw “file error!”

3) 可以抛出类类型的对象

class FileError{};

// FileError ex;

// throw ex;

以上两行等同于下一行代码

throw FileError();

4、2 异常捕获

try{

       可能发生异常的语句;

}

catch(异常类型1){

       针对异常类型1的处理

}

catch(异常类型2){

       针对异常类型2的处理

}

……

catch(….) {

       针对其他异常的处理

}

举例:

#include<iostream>

#include<cstdio>

#include<csetjmp>

using namespace std;

class A{

public:

   A(void){

       cout<<"A::A()"<<endl;

    }

   ~A(void){

       cout<<"A::~A()"<<endl;

    }

};

int func3(void){

    Aa;

   FILE* fp=fopen("none.txt","r");

   if(fp==NULL){

       throw -1;//抛出异常 (存在安全区,堆区和栈区之间)

    }

   //...操作

   fclose(fp);

   return 0;

}

int func2(void){

    Aa;

   func3();

   //...其他操作

   return 0;

}

int func1(void){

    Aa;

   func2();

   //...其它操作

}

int main(void){

   try{

       func1();

       //....(不被执行)

    }

   catch(int ex){

       cout<<"file open error!"<<endl;

       return -1;

    }

   return 0;

}

tarena@tarena-virtual-machine:~/day47$./a.out

A::A()

A::A()

A::A()

A::~A()

A::~A()

A::~A()

file open error!

另一种方式:

#include<iostream>

#include<cstdio>

#include<csetjmp>

using namespace std;

class FileError{

public:

   FileError(void){}

   FileError(const string& file,int line):

       m_file(file),m_line(line){

           cout<<"出错位置:"<<m_file<<","<<m_line<<endl;

       }

private:

   string m_file;

   int m_line;

};

class A{

public:

   A(void){

       cout<<"A::A()"<<endl;

    }

   ~A(void){

       cout<<"A::~A()"<<endl;

    }

};

int func3(void){

    Aa;

   FILE* fp=fopen("none.txt","r");

   if(fp==NULL){

       throw FileError(__FILE__,__LINE__);//预定义的宏

       throw FileError();

       throw -1;//抛出异常  (存在安全区,堆区和栈区之间)

    }

   //...操作

   fclose(fp);

   return 0;

}

int func2(void){

    Aa;

   func3();

   //...其他操作

   return 0;

}

int func1(void){

    Aa;

   func2();

   //...其它操作

}

int main(void){

   try{

       func1();

       //....(不被执行)

    }

   catch(int ex){

       cout<<"file open error!"<<endl;

       return -1;

    }

   catch(FileError ex){

       cout<<"fileError!"<<endl;

       return -1;

    }

   return 0;

}

tarena@tarena-virtual-machine:~/day47$./a.out

A::A()

A::A()

A::A()

出错位置:3.cpp,29

A::~A()

A::~A()

A::~A()

fileError!

4、3 catch字句根据异常对象的类型自上而下顺序匹配,而不是最优匹配,因此对子类的异常捕获不要放在对基类类型的异常捕获的后面,否则子类异常将被基类的catch子句提前捕获

  1#include<iostream>

  2using namespace std;

  3class A{};

  4class B:public A{};

  5void func(void){

 6     throw B();

 7     //throw A();

  8 }

  9int main(void){

 10    try{

 11        func();

 12     }

 13//应该把子类的捕获写在前面,基类写在后面

 14//由于向上造型的原因,基类可以匹配子类,而子类不会匹配基类

 15    catch(B& ex){

 16         cout<<"捕获到异常B"<<endl;

 17        return -1;

 18    }  

 19    catch(A& ex){//加引用防止拷贝构造,提高效率

 20        cout<<"捕获到异常A"<<endl;

 21        return -1;

 22    }  

 23} 

tarena@tarena-virtual-machine:~/day47$./a.out

捕获到异常A

4、3 标准异常类

class exeception{

public:

       exeception()throw() {}

       virtual~exeception() throw() {}

       virtualconst char* what() const throw();

};

class A: public exeception{

public:

       constchar* what() const throw(){

       cout<<”A::error”<<endl;

}

};

class B: public exeception{

public:

       constchar* what() const throw(){

       cout<<”B::error”<<endl;

}

};

int main(void){

       try{

       //可能发生A、B …异常

       throwA();

}

catch(exeception&ex){

ex.what();  //利用多态的虚函数就可以知道错在哪

}

}

标准库提供的异常:

  1#include<iostream>

  2using namespace std;

  3class FileError:public exception{

  4public:

 5     const char* what(void)constthrow(){

 6         return "file openerror!";

 7     }

  8};

  9int main(void){

 10    try{

 11        char*p=new char[0xffffffff];//肯定new失败

 12     }

 13    catch(exception& ex){

 14        cout<<ex.what()<<endl;

 15        return -1;

 16     }

 17    return 0;

 18 }

tarena@tarena-virtual-machine:~/day47$./a.out

std::bad_alloc

  1#include<iostream>

  2using namespace std;

  3//继承标准异常,覆盖what函数,较少catch语句分支

  4class FileError:public exception{

  5public:

 6     const char* what(void)constthrow(){

 7         return "file openerror!";

 8     }

  9};

 10int main(void){

 11    try{

 12        throw FileError();

 13        char*p=new char[0xffffffff];//肯定new失败

 14     }

 15    catch(exception& ex){

 16        cout<<ex.what()<<endl;

 17        return -1;

 18     }

 19    return 0;

 20 }

tarena@tarena-virtual-machine:~/day47$./a.out

file open error!

5、 异常说明

1) 可以在函数原型中增加异常说明,说明该函数所可能抛出的异常类型

2) 函数的异常说明是一种承诺,表示该函数所抛出的异常不会超出所说明的范围,如果抛出了异常说明以外的异常对象,该异常无法被捕获,而是继续向上抛出,最终被系统捕获,终止进程

返回类型 函数名(形参表) throw(异常类型表)

void func(string file,int size) throw(FileError,MemoryError){….}

int main(void){

try{

       func(file,size);

}

catch(FileError){….}

catch(MemoryError){…..}

catch(int&ex){} //无法捕获

}

3) 两种极端形式

---->不写异常说明,表示可以抛出任何异常

----->空异常说明,throw():表示不会抛出任何异常

#include<iostream>

using namespace std;

class FileError{};

class MemoryError{};

voidfoo(void)/*throw(FileError,MemoryError)*/{

   throw MemoryError();

 /*throw FileError();

   throw -1;  */

}

int main(void){

   try{

       foo();

    }

   catch(FileError& ex){

       cout<<"File error!"<<endl;

       return -1;

    }

   catch(MemoryError& ex){

       cout<<"MemoryError!"<<endl;

       return -1;

    }

   catch(...){

       cout<<"other error!"<<endl;

       return -1;

    }

   return 0;

}

tarena@tarena-virtual-machine:~/day47$./a.out

MemoryError!

4)如果函数的声明和定义分开书写,要保证异常说明一致,而异常说明类型和顺序无所谓。

#include<iostream>

using namespace std;

class FileError{};

class MemoryError{};

void foo(void)throw(FileError,MemoryError);

int main(void){

   try{

       foo();

    }

   catch(FileError& ex){

       cout<<"File error!"<<endl;

       return -1;

    }

   catch(MemoryError& ex){

       cout<<"MemoryError!"<<endl;

       return -1;

    }

   catch(...){

       cout<<"other error!"<<endl;

       return -1;

    }

   return 0;

}

void foo(void)throw(FileError,MemoryError){

   throw MemoryError();

 /*throw FileError();

   throw -1;  */

}

6、 构造函数中的异常

1)构造抛出异常,对象将会不完整构造,这样的对象的析构函数永远不会被调用。

2)因此在构造函数抛出异常之前,需要手动销毁在异常产生之前动态分配的资源。

#include<iostream>

#include<cstdio>

using namespace std;

class A{

public:

   A(void){cout<<"A::A()"<<endl;}

   ~A(void){cout<<"A::~A()"<<endl;}

};

class B{

public:

    B(void):m_a(newA){

       FILE* fp=fopen("none.text","r");

       if(fp==NULL){

           delete m_a;  //出现异常执行这个delete

           throw -1;

       }

       //...操作

       fclose(fp);

    }

   ~B(void){    //不出现异常的话执行这个delete

       delete m_a;

    }

private:

   A* m_a;

};

int main(void){

   try{

       B b;

    }

   catch(int& ex){

       cout<<"错误信息:"<<ex<<endl;

       return -1;

    }

   return 0;

}

tarena@tarena-virtual-machine:~/day47$./a.out

A::A()

A::~A()

错误信息:-1

7、 析构函数中的异常(了解)

//析构函数最好不要抛出异常

#include<iostream>

using namespace std;

class A{

public:

   void func(void){

       throw -1;

    }

   ~A(void){

       throw -2;

    }

};

int main(){

    Aa;

   try{

       a.func();

    }

   catch(int& ex){

       cout<<"异常:"<<ex<<endl;

       return -1;

    }

   return 0;

}

tarena@tarena-virtual-machine:~/day47$./a.out

异常:-1

二、            I/O

1、主要的I/O流类

                   ios

             /             \

          istream        ostream

       /  |   \         / |   \

 istrstream ifstream iostream of…..

2、格式化I/O

1)格式化函数

  1 #include<iostream>

  2 #include<cmath>

  3 using namespace std;

  4 int main(void){

  5    cout<<sqrt(20000)<<endl;//默认保留6位有效数字

  6    cout.precision(10);//取10位有效数字

  7    cout<<sqrt(20000)<<endl;

  8    cout.setf(ios::scientific);//科学计数法

  9    cout<<sqrt(20000)<<endl;

 10    cout<<'[';

 11    cout.width(10);//10个字符域宽

 12    cout.fill('#');//空白位置用“#”填充

 13    cout.setf(ios::showpos);//显示正负号

 14    cout.setf(ios::internal);//内插对齐(符号靠左,数据靠右)

 15    cout<<12345;

 16     cout<<']'<<endl;

 17    return 0;

 18 }

tarena@tarena-virtual-machine:~/day47$./a.out

141.421

141.4213562

1.4142135624e+02

[+####12345]

2)流控制符

  1#include<iostream>

  2#include<cmath>

  3#include<iomanip>

  4using namespace std;

  5int main(void){

  6     cout<<sqrt(20000)<<endl;//默认保留6位有效数字

 7    cout<<setprecision(10)<<//精度改为10

 8         sqrt(20000)<<endl;

 9    cout<<scientific<<sqrt(20000)<<endl;//科学计数法

 10    cout<<'[';

 11    cout<<setw(10)<<setfill('#')<<showpos<<internal<<12345;

 12     cout<<']'<<endl;

 13    cout<<'[';

 14    cout<<setw(12)<<hex<<left<<showbase<<100;

 15    cout<<']'<<endl;

 16    return 0;

 17 }

tarena@tarena-virtual-machine:~/day47$./a.out

141.421

141.4213562

1.4142135624e+02

[+####12345]

[0x64########]

3)字符串流

#include<strstream>

istrstream、ostrstream、strstream

 

#include<sstream>  (新的)

istringstream、ostringstream、stringstream

 

输入:

  1 #include<iostream>

  2 #include<cstdio>

  3 #include<sstream>

  4 using namespace std;

  5 int main(){

  6    int i=1234;

  7     doubled=3.14;

  8    char c='T';

  9    char s[]="hello world!";

 10 /* char buf[1024]={};//数据缓存区

 11    sprintf(buf,"%d %g %c %s",i,d,c,s);//把数据放到内存缓存区buf

 12    cout<<buf<<endl;  */

 13    ostringstream oss;//输出流

 14    oss<<i<<' '<<d<<' '<<c<<' '<<s;//把数据插入到Oss所指向的内存缓存区

 15    cout<<oss.str()<<endl;//转换成string格式

 16    return 0;

 17 }

tarena@tarena-virtual-machine:~/day47$./a.out

1234 3.14 T helloworld!

 

读取:

  1 #include<iostream>

  2 #include<sstream>

  3 using namespace std;

  4 int main(){

  5    int a;

  6    double d;

  7    char x;

  8    char y[100];

  9    istringstream iss;

 10    iss.str("100 2.34 A hello world");

 11    iss>>a>>d>>x>>y;//输入流

 12    cout<<a<<','<<d<<','<<x<<','<<y<<endl;

 13    //和sscanf作用一样,准备一个数据缓存区,用sscanf从数据缓存区里读数据

 14    return 0;

 15 }

100,2.34,A,hello

4)文件流

#include<fstream>

ifstream、ofstream、fstream

 

向文件输出内容:

  1 #include<iostream>

  2 #include<fstream>

  3 using namespace std;

  4 int main(){

  5    //向文件输出:fprintf

  6    ofstream ofs("file.txt");

  7    ofs<<1234<<' '<<56.78<<' '<<"helloworld"<<endl;

  8    ofs.close();

  9 }

tarena@tarena-virtual-machine:~/day47$./a.out

tarena@tarena-virtual-machine:~/day47$cat file.txt

1234 56.78 hello world

 

从文件读取内容:

  1 #include<iostream>

  2 #include<fstream>

  3 using namespace std;

  4 int main(){

  5    //向文件输出:fprintf

  6 /*ofstream ofs("file.txt");

  7    ofs<<1234<<' '<<56.78<<' '<<"helloworld"<<endl;

  8    ofs.close();  */

  9

 10    //从文件输入:fscanf

 11    int i;

 12    double d;

 13     strings;

 14    ifstream ifs("file.txt");//已存在

 15    //从文件中提取一个整型数放入i中,提取浮点数放入d中,提取字符串放入s中

 16    ifs>>i>>d>>s;//跟file文件的内容一一对应

 17    cout<<i<<','<<d<<','<<s<<endl;

 18    ifs.close();

 19    return 0;

 20 }

1234,56.78,hello

5)随机读写

istream& istream::

seekg(off_type offset, ios::seekdir origin)

ostream& ostream::

seekg(off_type offset, ios::seekdir origin)

offset:偏移位置,整数向后偏移,负数向前偏移

origin:取值:

       ios::beg –从文件头偏移

       ios::cur –从当前位置偏移

       ios::end –从文件尾偏移

  1 #include<iostream>

  2 #include<fstream>

  3 using namespace std;

  4 int main(){

  5    fstream fs("file.txt",ios::in|ios::out);//指定读写权限

  6    fs<<"0123456789";//向文件写入数据

  7    fs.seekp(-3,ios::cur);

  8    fs<<"XYZ";//file的文件内容:0123456XYZ

  9

 10    fs.seekg(3,ios::beg);

 11    string str;

 12     fs>>str;

 13    cout<<str<<endl;// 3456XYZ

 14

 15    fs.seekp(ios::beg);

 16    fs<<"ABC";//file的文件内容ABC3456XYZ

 17    return 0;

 18 }

6)二进制I/O(fread / fwrite  、 read / write)

 

二进制写:ostream& ostream::write(constchar * buffer , size_t num)

二进制读:istream& istream::read(char * buffer , streamsize num)

二进制写举例:

  1 #include<iostream>

  2 #include<fstream>

  3 using namespace std;

  4 int main(void){

  5    ofstream ofs("123.txt");

  6    char wbuf[]="tangzihaoo@126.com";

  7    ofs.write(wbuf,sizeof(wbuf));

  8    ofs.close();

  9    return 0;

 10 }

tarena@tarena-virtual-machine:~/day47$cat 123.txt

tangzihaoo@126.comtarena

二进制读举例:

  1 #include<iostream>

  2 #include<fstream>

  3 using namespace std;

  4 int main(void){

  5    ofstream ofs("123.txt");

  6    char wbuf[]="tangzihaoo@126.com";

  7    ofs.write(wbuf,sizeof(wbuf)); //向文件写入数据

  8    ofs.close();

  9    

 10    ifstream ifs("123.txt");

 11    char rbuf[100]={0};

 12    ifs.read(rbuf,sizeof(rbuf));//从文件读数据到rbuf里

 13    cout<<rbuf<<endl;//输出buf到屏幕  tangzihaoo@126.com

 14    return 0;

 15 }

0 0
原创粉丝点击