C++ 语法基础(五)

输入输出概述

  • 输入输出: 程序与外部设备交换信息。

    可分为基于控制台的I/O,基于文件的I/O,基于字符串的I/O。

  • 数据流: C++ 把输入输出看作一个数据流。

    • 输入流:外围设备流向内存的数据
    • 输出流:内存流向外围设备的数据
  • C++ 中相关的头文件如下表所示。

 

输入输出缓冲 

  • I/O对象: C++ 程序不能直接与输入输出设备交换信息,而需要通过一个对象实现。对象是输入输出设备在程序中的代理。

    每个 I/O 对象管理一个缓冲区,用于储存读写得数据。

  • 格式: >> 从输入缓冲区取数据存入变量;<< 将数据放入输出缓冲区。

  • 缓冲区刷新:

    • 程序正常结束:清空所有输出缓冲区,真正输出内容。
    • 缓冲区满:在写入下一个值之前,会刷新缓冲区。
    • 强制刷新:endl 显式地刷新缓冲区, unitbuf 操作符设置流的内部状态清空缓冲区。
    • 关联输入输出流:在读输入流时,将刷新其关联的输出缓冲区。 

 

 整数和实数的输入输出

设置整数基数

  • hex 十六进制 oct 八进制 dec 十进制
  • setbase(进制数),只允许 8、10、16 进制。
  • 头文件 iomanip
  • 流的基数值只有被显式更改才会发生变化,否则会一直沿用原有基数。

设置浮点数精度

  • 精度: 指实型数的有效位数。
  • 设置方法:
    • 流操作符:setprecision(位数)
    • 流的成员函数:precision(位数)
  • 一旦设置了精度,将影响所有输出的浮点数的精度,直到下一个设置精度的操作为止。

 设置域宽

  • 精度: 指实型数的有效位数。
  • 设置方法:
    • 流操作符:setprecision(位数)
    • 流的成员函数:precision(位数)
  • 一旦设置了精度,将影响所有输出的浮点数的精度,直到下一个设置精度的操作为止。

 文件的概念

  • 文件: 是驻留在外储存器上,具有标识名的一组信息的集合,用来永久保存数据。

  • 与文件相关的概念: 数据项(字段),记录,文件,数据库。

  • 外存: 程序可以直接访问内存中的数据,但不能直接访问外存。

    外存和程序之间的 I/O 需要经过输入输出缓冲。外存到缓冲区由操作系统控制,缓冲区到内存由程序控制。

    • 磁道:存储信息的场所。

    • 柱面:不同盘片的同一磁道。

    • 扇区:磁道划分成扇区,储存一个数据块。

流式文件

  • 文件和流: C++ 把每个文件都看成一个有序的字节流,每个文件以 EOF (end-of-file marker) 结束
  • ASCII 文件: 也被称为文本文件,将文件中的每个字节解释成一个字符的ASCII值,可以直接显示在显示器上
  • 二进制文件: 将文件中的每个字节仅看成是一个二进制比特串,由程序解释比特串的含义

 

文件的访问过程 

  • 访问文件步骤: 定义流对象,打开文件,操作文件数据,关闭文件。

  • 定义流对象:ifstream 、ofstream 、fstream

  • 打开和关闭文件: 成员函数 open 以及 close

  • 文件打开模式:

  • 检查是否打开成功: 打开失败值为 false、fail 函数、is_open 函数。

 文件实例(上)

  • 图书馆的书目管理系统

    • 功能: 初始化系统、添加书、借书、还书、显示书库信息
    • 每本书需要记录的信息
      • 馆藏号(整型数):要求自动生成
      • 书名(最长20个字符的字符串)
      • 借书标记。借书标记中记录的是借书者的借书证号,假设也是整型数。
  • 文件设计:文件头存书的数量,然后顺序存放书本数据信息

  • book类设计

    • 数据成员:馆藏号、书名、借书标记
    • 成员函数:构造函数、借书还书、显示书的详细信息
  • book类实现

Book::Book(const char *s, int totalNo)
{
    no = totalNo;
    borrowed = 0;
    strcpy(name, s);
}
void Book::borrow(int readerNo)
{
    if (borrowed != 0)
        cerr << "本书已被借,不能重复借\n";
    else
        borrowed = readerNo;
}
void Book::Return()
{
    if (borrowed == 0)
        cerr << "本书没有被借,不能还\n";
    else
        borrowed = 0;
}
void Book::display() const
{
    cout << setw(10) << no << setw(20) << name << setw(10) << borrowed << endl;
}

文件实例(下) 

  • 系统分解

    • 每个功能用一个函数实现
    • Main函数:显示菜单,根据用户的选择调用相应的函数
  • Main函数

int main()
{
    int selector;
    while (true)
    {
        cout << "0 -- 退出\n";
        cout << "1 -- 初始化文件\n";
        cout << "2 -- 添加书\n";
        cout << "3 -- 借书\n";
        cout << "4 -- 还书\n";
        cout << "5 -- 显示所有书目信息\n";
        cout << "请选择(0-5):";
        cin >> selector;
        if (selector == 0)
            break;
        switch (selector)
        {
        case 1:
            initialize();
            break;
        case 2:
            addBook();
            break;
        case 3:
            borrowBook();
            break;
        case 4:
            returnBook();
            break;
        case 5:
            displayBook();
            break;
        }
    }
    return 0;
}

Initialize的实现

 void initialize() 
  {
         ofstream outfile("book");	
  	
         outfile.close();
  }

​​​​​addBook的实现

 void addBook() 
  {
         char ch[20];
         Book *bp;
         ofstream outfile("book", ofstream::app | ofstream::ate | ofstream::binary);
   
         long int no = outfile.tellp() / sizeof(Book) + 1 ;
   
         cout << "请输入书名:"; 
         cin >> ch;
         bp = new Book(ch, no);
   
         outfile.write( reinterpret_cast<const char *>(bp), sizeof(*bp));
        delete bp;
      
        outfile.close();
  }

borrowBook的实现

 void borrowBook() 
  {
      int bookNo, readerNo;
      fstream iofile("book",  fstream::binary);				
      Book bk;
   
      cout << "请输入书号和读者号:";      cin >> bookNo >> readerNo;
   
      iofile.seekg((bookNo - 1) * sizeof(Book));	
      iofile.read( reinterpret_cast<char *> (&bk), sizeof(Book) );   
  
      bk.borrow(readerNo);	 
      iofile.seekp((bookNo - 1) * sizeof(Book));	
      iofile.write(reinterpret_cast<const char *>(&bk), sizeof(Book));	
   
      iofile.close();
  }

returnBook的实现

  void returnBook() 
  {
       int bookNo;
       fstream iofile("book", ofstream::binary );
       Book bk;
   
       cout << "请输入书号:";    
       cin >> bookNo ;
   
      iofile.seekg((bookNo - 1) * sizeof(Book));
      iofile.read(reinterpret_cast<char *> (&bk), sizeof(Book));
   
      bk.Return();		
     
      iofile.seekp((bookNo - 1) * sizeof(Book));
      iofile.write( reinterpret_cast<const char *>(&bk), sizeof(Book));
   
      iofile.close();
   }

 displayBook的实现

  void displayBook() 
  {
      ifstream infile("book", ofstream::binary);
      Book bk;
   
      infile.read(reinterpret_cast<char *> (&bk), sizeof(Book));
   
      while (!infile.eof()) {                             
            bk.display();                                   
           infile.read(reinterpret_cast<char *> (&bk), sizeof(Book));
      }
      infile.close();
  }

异常处理 

  • 异常: 程序执行过程中发生的一些不希望发生的事。

  • 传统解决方法: 在错误发生处就地处理。

    • 好处:程序员阅读代码可以直接看到错误情况。
    • 问题:因为错误污染代码使理解和维护变得困难。
  • 异常处理机制:分为异常抛出,以及异常捕获和处理两个部分。

异常抛出 

  • 作用: 发生异常情况时将包含出错信息的对象抛出当前环境,发送给更大的环境得到更好的处理。

  • 格式: throw 异常对象;

  • 过程: 生成并初始化 throw 对象的副本,传回调用它的函数,退出函数,回收所有局部对象。类似函数的 return 过程。

捕获异常 

  • catch 捕获异常:
catch (<捕获的异常类型><可选对象>)
{
    //异常处理代码
}

注意:

  • 捕获以类型为标志,是否省略对象取决于需不需要在处理过程中引用该对象。

  • catch (...) 可以捕获任意类型的异常。

  • 抛出-捕获格式:

try
{
    //可能抛出异常的代码
}
catch (类型1 对象1)
{
    //处理情况1的代码
}
catch (类型2 对象2)
{
    //处理情况2的代码
}
#include <iostream>
#include <cmath>

using namespace std;

class DivideByZeroException
{
private:
    const char *message;

public:
    DivideByZeroException() : message("case 1") {}
    const char *what() { return message; }
};

int main()
{
    int a, b;
    try
    {
        cin >> a >> b;
        if (b == 0)
        {
            throw DivideByZeroException();
        }
        cout << a / b;
    }
    catch (const char *)
    {
        cout << "case 2\n";
    }
    catch (DivideByZeroException)
    {
        cout << "case 3\n";
    }
    catch (...)
    {
        cout << "case 4\n";
    }
    return 0;
}
输入:100 0


正确答案:
case 3
2.

以下是一段带异常处理的除法计算器,请按照规范的异常处理过程补全它。(空1)

#include <iostream>
#include <cmath>

using namespace std;

class DivideByZeroException
{
private:
    _____(1)_____;

public:
    DivideByZeroException() : message("attempt to divide by zero") {}
    const char *what() { return message; }
};

double Div(int x, int y)
{
    if (_____(2) _____)
        _____(3)_____;
    return 1.0 * x / y;
}

int main()
{
    int number1, number2;
    double result;

    cout << "Enter two integers (end-of-file to end): ";
    while (cin >> number1 >> number2)
    {
        try
        {
            result = Div(number1, number2);
            cout << "The quotient is: " << result << endl;
        }
        catch (_____(4) _____ ex)
        {
            cout << "Exception occurred: " << _____(5) _____ << '\n';
        }
        cout << "\nEnter two integers (end-of-file to end): ";
    }
    cout << endl;
}

答案解释
const char* message
答案解释
y == 0

4.

(3)


答案解释
throw DivideByZeroException();

5.

(2)


答案解释
DivideByZeroException

6.

(5)


答案解释
ex.what()

异常规格说明

传统函数声明:

void f(); //函数可以抛出任何异常

带异常规格的函数声明:

void f() throw(); //函数不会有异常抛出。
void f() throw(toobig, toosmall, divzero);  //函数会抛出toobig, toosmall, divzero三种异常。

通用格式为

返回类型 函数名(形式参数表) throw(异常类型);
  • C++11 的改进

    • 摒弃异常规格声明。
    • 关键字 noexceptvoid f() noexcept; 表示函数不抛出。
    • 运算符 noexcept 判断表达式是否会抛异常。
1.

读程序,写结果。

#include <iostream>

using namespace std;

class up{};
class down{};

void f(int i) throw(up, down);

int main()
{
    for (int i = 1; i <= 10; ++i)
        try
        {
            f(i);
        }
        catch (up)
        {
            cout << "up catched" << endl;
        }
        catch (down)
        {
            cout << "down catched" << endl;
        }
    return 0;
}

void f(int i) throw(up, down)
{
    if (i % 3 == 0)
        throw up();
    if (i % 4 == 0)
        throw down();
}
正确答案:
up catched
down catched
up catched
down catched
up catched

类模板

  • 类模板的定义格式:

    template <模板的形式参数表>
    class 类名{...};
    
  • 模板的形式参数可以是类型形参(用关键字class或typename开始,后面是形式参数名)和非类型形参。

  • 在类定义外面定义成员函数格式:

    template <模板的形式参数表>
    返回类型 类名<各形式参数>::函数名(形式参数表)
填空题
1.

类模板A的声明如下。若要在类定义外定义f()函数,需要如何实现?请给出除函数体外的代码(函数体内用/*函数体*/代替)。

template <class T, typename U>
class A
{
public:
    T a, b;

    U f(const T &a, const T &b);
};
答案解释
template <class T, typename U>
U A::f()
{
    /*函数体*/
}

 类模板的实例化

  • 类模板对象的实例化格式:

    类模板名<模板的实际参数表> 对象表;
填空题
1.

stl中的std::map的定义是

template < class Key,                                     // map::key_type
           class T,                                       // map::mapped_type
           class Compare = less<Key>,                     // map::key_compare
           class Alloc = allocator<pair<const Key,T> >    // map::allocator_type
           > class map;
以下实例化正确的有:(按照大写字母从小到大排列无空格填写)

A. std::map<std::string,int>

B. std::map<"abcd",int>

C. std::map<int,std::string>

D. std::map<7,'c'>
正确答案:
AC

猜你喜欢

转载自blog.csdn.net/qq_43215597/article/details/125237647