-
输入输出: 程序与外部设备交换信息。
可分为基于控制台的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(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 (&bk), sizeof(Book) );
bk.borrow(readerNo);
iofile.seekp((bookNo - 1) * sizeof(Book));
iofile.write(reinterpret_cast(&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 (&bk), sizeof(Book));
bk.Return();
iofile.seekp((bookNo - 1) * sizeof(Book));
iofile.write( reinterpret_cast(&bk), sizeof(Book));
iofile.close();
}
displayBook的实现
void displayBook()
{
ifstream infile("book", ofstream::binary);
Book bk;
infile.read(reinterpret_cast (&bk), sizeof(Book));
while (!infile.eof()) {
bk.display();
infile.read(reinterpret_cast (&bk), sizeof(Book));
}
infile.close();
}
异常处理
-
异常: 程序执行过程中发生的一些不希望发生的事。
-
传统解决方法: 在错误发生处就地处理。
- 好处:程序员阅读代码可以直接看到错误情况。
- 问题:因为错误污染代码使理解和维护变得困难。
-
异常处理机制:分为异常抛出,以及异常捕获和处理两个部分。
-
作用: 发生异常情况时将包含出错信息的对象抛出当前环境,发送给更大的环境得到更好的处理。
-
格式: throw 异常对象;
-
过程: 生成并初始化 throw 对象的副本,传回调用它的函数,退出函数,回收所有局部对象。类似函数的 return 过程。
- catch 捕获异常:
catch (<捕获的异常类型><可选对象>)
{
//异常处理代码
}
注意:
-
捕获以类型为标志,是否省略对象取决于需不需要在处理过程中引用该对象。
-
catch (...) 可以捕获任意类型的异常。
-
抛出-捕获格式:
try
{
//可能抛出异常的代码
}
catch (类型1 对象1)
{
//处理情况1的代码
}
catch (类型2 对象2)
{
//处理情况2的代码
}
#include#include 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 2n"; } catch (DivideByZeroException) { cout << "case 3n"; } catch (...) { cout << "case 4n"; } return 0; } 输入:100 0 正确答案: case 3
2. 以下是一段带异常处理的除法计算器,请按照规范的异常处理过程补全它。(空1) #include异常规格说明#include 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 的改进
- 摒弃异常规格声明。
- 关键字 noexcept,void f() noexcept; 表示函数不抛出。
- 运算符 noexcept 判断表达式是否会抛异常。
1. 读程序,写结果。 #include类模板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 A { public: T a, b; U f(const T &a, const T &b); }; 答案解释 template U A::f() { }
-
类模板对象的实例化格式:
类模板名<模板的实际参数表> 对象表;
填空题
1.
stl中的std::map的定义是
template < class Key, // map::key_type
class T, // map::mapped_type
class Compare = less, // map::key_compare
class Alloc = allocator > // map::allocator_type
> class map;
以下实例化正确的有:(按照大写字母从小到大排列无空格填写)
A. std::map
B. std::map<"abcd",int>
C. std::map
D. std::map<7,'c'>
正确答案:
AC



