第 十一 章 流类库与输入/输出
11-1 什么叫做流?流的提取和插入是指什么?I/O流在C++中起着怎样的作用?
解:
流是一种抽象,它负责在数据的生产者和数据的消费者之间建立联系,并管理数据的流动,一般意义下的读操作在流数据抽象中被称为(从流中)提取,写操作被称为(向流中)插入。操作系统是将键盘、屏幕、打印机和通信端口作为扩充文件来处理的,I/O流类就是用来与这些扩充文件进行交互,实现数据的输入与输出。
11-2 cerr和clog有何区别?
解:
cerr 标准错误输出,没有缓冲,发送给它的内容立即被输出,适用于立即向屏幕输出的错误信息;clog
类似于cerr,但是有缓冲,缓冲区满时被输出,在向磁盘输出时效率更高。
11-3
使用I/O流以文本方式建立一个文件test1.txt,写入字符“已成功写入文件!”,用其它字处理程序(例如windows的记事本程序Notepad)打开,看看是否正确写入。
解:
#include <fstream.h>
void main()
{
ofstream file1("test.txt");
file1 << "已成功写入文件!";
file1.close();
}
程序运行后test1.txt的内容为:已成功写入文件!
11-4 使用I/O流以文本方式打开上一题建立的文件test1.txt,读出其内容显示出来,看看是否正确。
解:
#include <fstream.h>
void main()
{
char ch;
ifstream file2("test.txt");
while (file2.get(ch))
cout << ch;
file2.close();
}
程序运行输出:
已成功写入文件!
11-5 使用I/O流以文本方式打开上题建立的文件test1.txt,在次此文件后面添加字符“已成功添加字符!”,然后读出整个文件的内容显示出来,看看是否正确。
解:
#include <fstream.h>
void main()
{
ofstream file1("test.txt",ios::app);
file1 << "已成功添加字符!";
file1.close ();
char ch;
ifstream file2("test.txt");
while (file2.get(ch))
cout << ch;
file2.close();
}
程序运行输出:
已成功写入文件!已成功添加字符!
11-6
定义一个dog类,包含体重和年龄两个成员变量及相应的成员函数,声明一个实例dog1,体重为5,年龄为10,使用I/O流把dog1的状态写入磁盘文件,再声明另一个实例dog2,通过读文件把dog1的状态赋给dog2。分别使用文本方式和二进制方式操作文件,看看结果有何不同;再看看磁盘文件的ASCII码有何不同。
解:
以两种方式操作,程序运行结果一样,但磁盘文件的ASCII码不同,使用二进制方式时,磁盘文件的ASCII码为05 00 00 00 0A 00 00
00,使用文本方式时,磁盘文件的ASCII码为05 00 00 00 0D 0A 00 00 00,这是因为此时系统自动把0A转换为了0D 0A。
#include <fstream.h>
class dog
{
public:
dog(int weight, long days):itsWeight(weight),
itsNumberDaysAlive(days){}
~dog(){}
int GetWeight()const { return itsWeight; }
void SetWeight(int weight) { itsWeight = weight; }
long GetDaysAlive()const { return itsNumberDaysAlive; }
void SetDaysAlive(long days) { itsNumberDaysAlive = days; }
private:
int itsWeight;
long itsNumberDaysAlive;
};
int main() // returns 1 on error
{
char fileName[80];
cout << "Please enter the file name: ";
cin >> fileName;
ofstream fout(fileName);
// ofstream fout(fileName,ios::binary);
if (!fout)
{
cout << "Unable to open " << fileName << " for writing.\n";
return(1);
}
dog Dog1(5,10);
fout.write((char*) &Dog1,sizeof Dog1);
fout.close();
ifstream fin(fileName);
// ifstream fin(fileName,ios::binary);
if (!fin)
{
cout << "Unable to open " << fileName << " for reading.\n";
return(1);
}
dog Dog2(2,2);
cout << "Dog2 weight: " << Dog2.GetWeight() << endl;
cout << "Dog2 days: " << Dog2.GetDaysAlive() << endl;
fin.read((char*) &Dog2, sizeof Dog2);
cout << "Dog2 weight: " << Dog2.GetWeight() << endl;
cout << "Dog2 days: " << Dog2.GetDaysAlive() << endl;
fin.close();
return 0;
}
程序运行输出:
Please enter the file name: a
Dog2 weight: 2
Dog2 days: 2
Dog2 weight: 5
Dog2 days: 10
11-7 观察下面的程序,说明每条语句的作用,看看程序执行的结果。
#include <iostream>
using namespace ::std;
void main()
{
ios_base::fmtflags original_flags = cout.flags(); //1
cout<< 812<<'|';
cout.setf(ios_base::left,ios_base::adjustfield); //2
cout.width(10); //3
cout<< 813 << 815 << '\n';
cout.unsetf(ios_base::adjustfield); //4
cout.precision(2);
cout.setf(ios_base::uppercase|ios_base::scientific); //5
cout << 831.0 ;
cout.flags(original_flags); //6
}
解:
//1保存现在的格式化参数设置,以便将来恢复这些设置;
//2 把对齐方式由缺省的右对齐改为左对齐;
//3 把输出域的宽度由缺省值0改为10;
//4 清除对齐方式的设置;
//5 更改浮点数的显示设置;
//6 恢复原来的格式化参数设置。
程序运行输出:
812|813 815
8.31E+02
11-8 提示用户输入一个十进制整数,分别用十进制、八进制和十六进制形式输出。
解:
#include <iostream.h>
void main() {
int n;
cout << "请输入一个十进制整数:";
cin >> n;
cout << "这个数的十进制形式为:" << dec << n << endl;
cout << "这个数的八进制形式为:" << oct << n << endl;
cout << "这个数的十六进制形式为:" << hex << n << endl;
}
程序运行输出:
请输入一个十进制整数:15
这个数的十进制形式为:15
这个数的八进制形式为:17
这个数的十六进制形式为:f
11-9 编写程序实现如下功能:打开指定的一个文本文件,在每一行前加行号。
解:
//b.cpp
#include <fstream.h>
#include <strstrea.h>
#include <stdlib.h>
void main(int argc, char* argv[])
{
strstream textfile;
{
ifstream in(argv[1]);
textfile << in.rdbuf();
}
ofstream out(argv[1]);
const int bsz = 100;
char buf[bsz];
int line = 0;
while(textfile.getline(buf, bsz)) {
out.setf(ios::right, ios::adjustfield);
out.width(1);
out << ++line << ". " << buf << endl;
}
}
编译后运行程序b text1.txt
运行前text1.txt的内容为:
aaaaaaaaaaaa
bbbbbbbbbbbb
cccccccccccc
dddddddddddd
eeeeeeeeeeee
ffffffffffff
gggggggggggg
hhhhhhhhhhhh
运行后text1.txt的内容为:
1. aaaaaaaaaaaa
2. bbbbbbbbbbbb
3. cccccccccccc
4. dddddddddddd
5. eeeeeeeeeeee
6. ffffffffffff
7. gggggggggggg
8. hhhhhhhhhhhh
第 十二 章 异常处理
12-1 什么叫做异常?什么叫做异常处理?
解:
当一个函数在执行的过程中出现了一些不平常的情况,或运行结果无法定义的情况,使得操作不得不被中断时,我们说出现了异常。异常通常是用throw关键字产生的一个对象,用来表明出现了一些意外的情况。我们在设计程序时,就要充分考虑到各种意外情况,并给与恰当的处理。这就是我们所说的异常处理。
12-2 C++的异常处理机制有何优点?
解:
C++的异常处理机制使得异常的引发和处理不必在同一函数中,这样底层的函数可以着重解决具体问题,而不必过多地考虑对异常的处理。上层调用者可以在适当的位置设计对不同类型异常的处理。
12-3 举例throw 、try、catch语句的用法?
解:
throw语句用来引发异常,用法为:
throw 表达式;
例如: throw 1.0E-10;
catch语句用来处理某中类型的异常,它跟在一个try程序块后面处理这个try程序块产生的异常,如果一个函数要调用一个可能会引发异常的函数,
并且想在异常真的出现后处理异常,就必须使用try语句来捕获异常。
例如:
try{
语句 //可能会引发多种异常
}
catch(参数声明1)
{
语句 //异常处理程序
}
12-4
设计一个异常Exception抽象类,在此基础上派生一个OutOfMemory类响应内存不足,一个RangeError类响应输入的数不在指定范围内,实现并测试这几个类。
解:
源程序:
#include <iostream.h>
class Exception
{
public:
Exception(){}
virtual ~Exception(){}
virtual void PrintError() = 0;
};
class OutOfMemory : public Exception
{
public:
OutOfMemory(){}
~OutOfMemory(){}
virtual void PrintError();
};
void OutOfMemory::PrintError()
{
cout << "Out of Memory!!\n";
}
class RangeError : public Exception
{
public:
RangeError(unsigned long number){BadNum = number;}
~RangeError(){}
virtual void PrintError();
virtual unsigned long GetNumber() { return BadNum; }
virtual void SetNumber(unsigned long number) {BadNum = number;}
private:
unsigned long BadNum;
};
void RangeError::PrintError()
{
cout << "Number out of range. You used " << GetNumber() << " !\n";
}
void fn1();
unsigned int * fn2();
void fn3(unsigned int *);
int main()
{
try
{
fn1();
}
catch (Exception& theException)
{
theException.PrintError();
}
return 0;
}
unsigned int * fn2()
{
unsigned int *n = new unsigned int;
if (n == 0)
throw OutOfMemory();
return n;
}
void fn1()
{
unsigned int *p = fn2();
fn3(p);
cout << "The number is : " << *p << endl;
delete p;
}
void fn3(unsigned int *p)
{
long Number;
cout << "Enter an integer(0~~1000): ";
cin >> Number;
if (Number > 1000 || Number < 0)
throw RangeError(Number);
*p = Number;
}
程序运行输出:
Enter an integer(0~~1000): 56
The number is : 56
Enter an integer(0~~1000): 2000
Number out of range. You used 2000 !
12-5 练习使用try、catch语句,在程序中用new分配内存时,如果操作未成功,则用try语句触发一个字符型异常,用catch语句捕获此异常。
解:
#include <iostream.h>
void main()
{
char *buf;
try
{
buf = new char[512];
if( buf == 0 )
throw "内存分配失败!";
}
catch( char * str )
{
cout << "有异常产生:" << str << endl;
}
}
12-6
定义一个异常类CException,有成员函数Reason(),用来显示异常的类型,定义函数fn1()触发异常,在主函数的try模块中调用fn1(),在catch模块中捕获异常,观察程序的执行流程。
解:
#include <iostream.h>
class CException
{
public:
CException(){};
~CException(){};
const char *Reason() const { return "CException类中的异常。"; }
};
void fn1()
{
cout<< "在子函数中触发CException类异常" << endl;
throw CException();
}
void main()
{
cout << "进入主函数" << endl;
try
{
cout << "在try模块中,调用子函数" << endl;
fn1();
}
catch( CException E )
{
cout << "在catch模块中,捕获到CException类型异常:";
cout << E.Reason() << endl;
}
catch( char *str )
{
cout << "捕获到其它类型异常:" << str << endl;
}
cout << "回到主函数,异常已被处理" << endl;
}
程序运行输出:
进入主函数
在try模块中,调用子函数
在子函数中触发CException类异常
在catch模块中,捕获到CException类型异常:CException类中的异常。
回到主函数,异常已被处理