C++笔记(六)——I/O&异常

荒凉一梦 提交于 2020-03-10 05:16:32
  1. iostream 标准库提供了 cin 和 cout 方法分别用于从标准输入读取流和向标准输出写入流。
    ·标准库 fstream定义了三个新的数据类型用于文件的访问。
    在这里插入图片描述
    ·open() 函数是 fstream、ifstream 和 ofstream 对象的一个成员。open() 成员函数的第一参数指定要打开的文件的名称和位置,第二个参数定义文件被打开的模式。可以把以上两种或两种以上的模式结合使用。
void open(const char *filename, ios::openmode mode);

在这里插入图片描述

//以写入模式打开文件,并希望截断文件,以防文件已存在
ofstream outfile;
outfile.open("file.dat", ios::out | ios::trunc );
//打开一个文件用于读写
ifstream  afile;
afile.open("file.dat", ios::out | ios::in );

· close() 函数用于关闭文件(通常应该在程序结束前关闭),close() 函数是 fstream、ifstream 和 ofstream 对象的一个成员。

void close();

·使用流插入运算符( << )向文件写入信息,使用的是 ofstream 或 fstream 对象,而不是 cout 对象;使用流提取运算符( >> )从文件读取信息,使用的是 ifstream 或 fstream 对象,而不是 cin 对象。

·istream 和 ostream 都提供了用于重新定位文件位置指针的成员函数,包括关于 istream 的 seekg(“seek get”)和关于 ostream 的 seekp(“seek put”)。
seekg 和 seekp参数通常是一个长整型。第二个参数可以用于指定查找方向查找方向可以是 ios::beg(默认的,从流的开头开始定位),也可以是 ios::cur(从流的当前位置开始定位),也可以是 ios::end(从流的末尾开始定位)。
文件位置指针是一个整数值,指定了从文件的起始位置到指针所在位置的字节数

// 定位到 fileObject 的第 n 个字节(假设是 ios::beg)
fileObject.seekg( n );
// 把文件的读指针从 fileObject 当前位置向后移 n 个字节
fileObject.seekg( n, ios::cur );
// 把文件的读指针从 fileObject 末尾往回移 n 个字节
fileObject.seekg( n, ios::end );
// 定位到 fileObject 的末尾
fileObject.seekg( 0, ios::end );
  1. C+程序只检查字节流,而不需要知道字节来自何方;通过使用流,C++程序处理输出的方式将独立于其去向。管理输入的步骤:将流与输入去向的程序关联起来;将流与文件连接起来
    ·缓冲区是用作中介的内存块,它是将信息从设备传输到程序或从程序传输给设备的临时存储工具。输出时,程序首先填满缓冲区,然后把整块数据传输给硬盘,并清空缓冲区,以备下一批输出使用,被称为刷新缓冲区(flushing the buffer)。
    在这里插入图片描述
    ·C++程序通常在用户按下回车键时刷新输入缓冲区;对于屏幕输出,C++程序通常在用户发送换行符时刷新输出缓冲区

  2. 插入运算符的所有化身的返回类型都是ostream&,即原型的格式如下。引用将指向用于调用该运算符的对象,这种特性使得能够通过插入来连接输出。

ostream& operator<<(type)

·除了各种operator<<()函数外,ostream类还提供了put()方法和write()方法,前者用于显示字符,后者用于显示字符串。cout是调用方法的对象,put()和write()是类成员函数。原型如下:

ostream& put(char);
basic ostream<charT,traits>& write(const char type*s,streamsize n)

·如果实现不能在所希望时刷新输出,可以使用两个控制符中的一个来强行进行刷新。
**控制符flush刷新缓冲区,而控制符endl刷新缓冲区,并插入一个换行符。**这两个控制符的使用方式与变量名相同。

cout <<"Hello,good-looking!"<< flush;
cout <<"Wait just a moment,please."<< endl;
  1. ios base类提供了一个setf()函数(用于set标记),能够控制多种格式化特性。这个类定义了多个常量,可用作该函数的参数。函数有两个原型。
fmtflags setf(fmtflags);
fmtflags setf(fmtflags,fmtflags);

在这里插入图片描述
·如下面的函数调用使cout显示末尾小数点:

cout.setf(ios base::showpoint);
  1. cin 或cout 对象包含一个描述流状态(stream state)的数据成员(从ios base类那里继承的)。流状态(被定义为iostate类型,而iostate是一种bitmask类型)由3个ios_base元素组成:eofbit、badbit或failbit,其中每个元素都是一位,可以是1(设置)或0(清除)
    在这里插入图片描述
    ·流状态设置。
    clear():调用将使用默认参数0,即清除全部3个状态位(eofbit、badbit和faillbit)
    参数为eofbit时,eofbit将被设置,另外两个状态位被清除
clear();
clear(eofbit);

只影响其参数中已设置的位,故调用将设置eofbit,而不会影响其他位。

setstate(eofbit);
  1. 文件写入与读取步骤(应包含头文件fstream):
//写入文件
//创建一个ofstream对象来管理输出流;
ofstream fout;//create an ofstream object named fout
//将该对象与特定的文件关联起来;
fout.open("jar.txt");//associate fout with jar.txt
//以使用cout的方式使用该对象。
fout << "Dull Data";

//读取文件
//创建一个ifstream对象来管理输入流;
ifstream fin;//create ifstream object called fin
//将该对象与特定的文件关联起来;
fin.open("jellyjar.txt");//open jellyjar.txt for reading
//以使用cin的方式使用该对象。
char ch;
fin>>ch;

//可以使用构造函数将创建对象和关联到文件合并成一条语句:
ofstream fout("jar.txt");//create fout object,associate it with jar.txt
ifstream fis("jamjar.txt");//create fis and associate with jamjar.txt
  1. C++ 异常处理涉及到三个关键字:try、catch、throw
    ·throw: 当问题出现时,程序会抛出一个异常。这是通过使用 throw 关键字来完成的。
    ·catch: 在想要处理问题的地方,通过异常处理程序捕获异常。catch 关键字用于捕获异常。
    ·try: 放置可能抛出异常的代码,try 块中的代码被称为保护代码。后面通常跟着一个或多个 catch 块。
    ·实例:除以零的异常。由于我们抛出了一个类型为 const char* 的异常,因此,当捕获该异常时,我们必须在 catch 块中使用 const char*。
#include <iostream>
using namespace std;
double division(int a, int b){
   if( b == 0 )
      throw "Division by zero condition!";
   return (a/b);
}
int main (){
   int x = 50;
   int y = 0;
   double z = 0;
   try{
     z = division(x, y);
     cout << z << endl;
   }catch (const char* msg){
     cerr << msg << endl;
   }
   return 0;
}

·执行throw语句类似于执行返回语句,因为它也将终止函数的执行;但throw不是将控制权返回给调用程序,而是导致程序沿函数调用序列后退,直到找到包含try块的函数
·catch块是一个处理程序,参数与引发异常匹配,然后执行处理程序的代码
·执行完try块中的语句后,如果没有引发任何异常,则程序跳过try块后面的catch块,直接执行处理程序后面的第一条语句。
·catch块的排列顺序应该与派生顺序相反。如果有一个异常类继承层次结构,应这样排列catch块:将捕获位于层次结构最下面的异常类的catch语句放在最前面,将捕获基类异常的catch语句放在最后面。
在这里插入图片描述
在这里插入图片描述

  1. exception头文件定义了exception类,C++可以把它用作其他异常类的基类。代码可以引发exception异常,也可以将exception类用作基类。

stdexcept异常类:头文件stdexcept定义了其他几个异常类。
·该文件定义了logic_error 和runtime_error类,它们都是以公有方式从exception派生而来的。这些类的构造函数接受一个string对象作为参数。

·异常类系列logic_error描述了典型的逻辑错误。每个类的名称指出了它用于报告的错误类型:domain_error(参数不在定义域);invalid_argument(函数传递意料之外的值);1ength_error(没有足够空间执行所需操作);out_of_bounds(索引错误)

·runtime_error异常系列描述了可能在运行期间发生但难以预计和防范的错误rangeerror(结果不在函数允许范围内,但未发生上溢或下溢);overflow_error(上溢);underflow_error(下溢)

bad_alloc异常和new:对于使用new导致的内存分配问题,C++的最新处理方式是让new引发bad_alloc异常(返回bad_alloc)。头文件new包含bad_alloc类的声明,它是从exception类公有派生而来的。

空指针和new:很多代码都是在new在失败时返回空指针时编写的。C++标准提供了一种在失败时返回空指针的new,其用法如下:

int* pi = new(std::nothrow)intint* pa = new(std::nowthrow)int[500]
  1. C++ 程序中的内存分为两个部分:
    栈:在函数内部声明的所有变量都将占用栈内存。
    堆:这是程序中未使用的内存,在程序运行时可用于动态分配内存
    ·使用new运算符为给定类型的变量在运行时分配堆内的内存,这会返回所分配的空间地址。
    ·不需要动态分配的内存空间时,可以使用 delete 运算符,删除之前由 new 运算符分配的内存
new data-type;//通用语法
double* pvalue  = NULL; // 初始化为 null 的指针
pvalue  = new double;   // 为变量请求内存

·如果自由存储区已被用完,可能无法成功分配内存,可以检查 new 运算符是否返回 NULL 指针。

double* pvalue  = NULL;
if( !(pvalue  = new double )){
   cout << "Error: out of memory." <<endl;
   exit(1);
}

·尽量不要使用 malloc() 函数。new 与 malloc() 函数相比,其主要的优点是,new 不只是分配了内存,它还创建了对象。
·几个示例:

double* pvalue  = NULL; // 初始化为 null 的指针
pvalue  = new double;   // 为变量请求内存
*pvalue = 29494.99;     // 在分配的地址存储值
cout << "Value of pvalue : " << *pvalue << endl;
delete pvalue;         // 释放内存

char* pvalue  = NULL;   // 初始化为 null 的指针
pvalue = new char[20]; // 为变量请求内存
delete [] pvalue;      // 删除 pvalue 所指向的数组

int **p;  
int i,j;   //p[4][8]
//开始分配4行8列的二维数据  
p = new int *[4];
for(i=0;i<4;i++){
    p[i]=new int [8];
}
for(i=0; i<4; i++){
    for(j=0; j<8; j++){
        p[i][j] = j*i;
    }
}  
//开始释放申请的堆  
for(i=0; i<4; i++){
    delete [] p[i];  
}
delete [] p; 
  1. 函数模板
template <class type> 
ret-type func-name(parameter list){
   // 函数的主体
}
template <typename T>

inline T const& Max (T const& a, T const& b){
    return a < b ? b:a;
}
  1. 类模板:可以使用一个逗号分隔的列表来定义多个泛型数据类型。
template <class type> 
class class-name{}

template <class T>
class Stack {
  private:
    vector<T> elems;     // 元素
 
  public:
    void push(T const&);  // 入栈
    void pop();               // 出栈
    T top() const;            // 返回栈顶元素
    bool empty() const{       // 如果为空则返回真。
        return elems.empty();
    }
};
  1. 预处理器是一些指令,指示编译器在实际编译之前所需完成的预处理。所有的预处理器指令都是以井号(#)开头,只有空格字符可以出现在预处理指令之前。预处理指令不是 C++ 语句,所以它们不会以分号(;)结尾。
    ·C++ 预处理指令包括:#include、#define、#if、#else、#line 等。
    ·#define 预处理指令用于创建符号常量,该符号常量通常称为
#define macro-name replacement-text

#define PI 3.14159

·条件编译示例:

#ifdef DEBUG
如果在#ifdef DEBUG前已经定义了符号常量 DEBUG,则会对cerr语句进行编译
   cerr <<"Variable x = " << x << endl;
#endif

·# 和 ## 运算符

#define MKSTR( x ) #x
int main (){
    cout << MKSTR(HELLO C++) << endl;//输出HELLO C++
    return 0;
}

#define concat(a, b) a ## b
int main(){
   int xy = 100;
   cout << concat(x, y);//输出100
   return 0;
}

·预定义宏
在这里插入图片描述

cout << "Value of __LINE__ : " << __LINE__ << endl;//输出6
cout << "Value of __FILE__ : " << __FILE__ << endl;//输出test.cpp
cout << "Value of __DATE__ : " << __DATE__ << endl;//输出Feb 02 2020
cout << "Value of __TIME__ : " << __TIME__ << endl;//输出20:20:20
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!