09-对象、继承和引用
8.2.6 对象、继承和引用
ostream和ofstream类凸现了引用的一个有趣属性。正如第6章介绍的,ofstream对象可以使用ostream类的方法,这使得文件输入/输出的格式与控制台输入/输出相同。使得能够将特性从一个类传递给另一个类的语言特性被称为继承,这将在第13章详细讨论。简单地说,ostream是基类(因为ofstream是建立在它的基础之上的),而ofstream是派生类(因为它是从ostream派生而来的)。派生类继承了基类的方法,这意味着ofstream对象可以使用基类的特性,如格式化方法precision()和setf()。
继承的另一个特征是,基类引用可以指向派生类对象,而无需进行强制类型转换。这种特征的一个实际结果是,可以定义一个接受基类引用作为参数的函数,调用该函数时,可以将基类对象作为参数,也可以将派生类对象作为参数。例如,参数类型为ostream &的函数可以接受ostream对象(如cout)或您声明的ofstream对象作为参数。
程序清单8.8通过调用同一个函数(只有函数调用参数不同)将数据写入文件和显示到屏幕上来说明了这一点。该程序要求用户输入望远镜物镜和一些目镜的焦距,然后计算并显示每个目镜的放大倍数。放大倍数等于物镜的焦距除以目镜的焦距,因此计算起来很简单。该程序还使用了一些格式化方法,这些方法用于cout和ofstream对象(在这个例子中为fout)时作用相同。
程序清单8.8 filefunc.cpp
//filefunc.cpp -- function with ostream & parameter
#include <iostream>
#include <fstream>
#include <cstdlib>
using namespace std;
void file_it(ostream & os, double fo, const double fe[],int n);
const int LIMIT = 5;
int main()
{
ofstream fout;
const char * fn = "ep-data.txt";
fout.open(fn);
if (!fout.is_open())
{
cout << "Can't open " << fn << ". Bye.\n";
exit(EXIT_FAILURE);
}
double objective;
cout << "Enter the focal length of your "
"telescope objective in mm: ";
cin >> objective;
double eps[LIMIT];
cout << "Enter the focal lengths, in mm, of " << LIMIT
<< " eyepieces:\n";
for (int i = 0; i < LIMIT; i++)
{
cout << "Eyepiece #" << i + 1 << ": ";
cin >> eps[i];
}
file_it(fout, objective, eps, LIMIT);
file_it(cout, objective, eps, LIMIT);
cout << "Done\n";
return 0;
}
void file_it(ostream & os, double fo, const double fe[],int n)
{
ios_base::fmtflags initial;
initial = os.setf(ios_base::fixed); // save initial formatting state
os.precision(0);
os << "Focal length of objective: " << fo << " mm\n";
os.setf(ios::showpoint);
os.precision(1);
os.width(12);
os << "f.l. eyepiece";
os.width(15);
os << "magnification" << endl;
for (int i = 0; i < n; i++)
{
os.width(12);
os << fe[i];
os.width(15);
os << int (fo/fe[i] + 0.5) << endl;
}
os.setf(initial); // restore initial formatting state
}
下面是该程序的运行情况:
Enter the focal length of your telescope objective in mm: 1800
Enter the focal lengths, in mm, of 5 eyepieces:
Eyepiece #1: 30
Eyepiece #2: 19
Eyepiece #3: 14
Eyepiece #4: 8.8
Eyepiece #5: 7.5
Focal length of objective: 1800 mm
f.l. eyepiece magnification
30.0 60
19.0 95
14.0 129
8.8 205
7.5 240
Done
下述代码行将目镜数据写入到文件ep-data.txt中:
file_it(fout, objective, eps, LIMIT);
而下述代码行将同样的信息以同样的格式显示到屏幕上:
file_it(cout, objective, eps, LIMIT);
程序说明
对于该程序,最重要的一点是,参数os(其类型为ostream &)可以指向ostream对象(如cout),也可以指向ofstream对象(如fout)。该程序还演示了如何使用ostream类中的格式化方法。下面复习(介绍)其中的一些,更详细的讨论请参阅第17章。
方法setf()让您能够设置各种格式化状态。例如,方法调用setf(ios_base::fixed)将对象置于使用定点表示法的模式;setf(ios_base::showpoint)将对象置于显示小数点的模式,即使小数部分为零。方法precision()指定显示多少位小数(假定对象处于定点模式下)。所有这些设置都将一直保持不变,直到再次调用相应的方法重新设置它们。方法width()设置下一次输出操作使用的字段宽度,这种设置只在显示下一个值时有效,然后将恢复到默认设置。默认的字段宽度为零,这意味着刚好能容纳下要显示的内容。
函数file_it()使用了两个有趣的方法调用:
ios_base::fmtflags initial;
initial = os.setf(ios_base::fixed); // save initial formatting state
...
os.setf(initial); // restore initial formatting state
方法setf()返回调用它之前有效的所有格式化设置。ios_base::fmtflags是存储这种信息所需的数据类型名称。因此,将返回值赋给initial将存储调用file_it()之前的格式化设置,然后便可以使用变量initial作为参数来调用setf(),将所有的格式化设置恢复到原来的值。因此,该函数将对象回到传递给file_it()之前的状态。
了解更多有关类的知识将有助于更好地理解这些方法的工作原理,以及为何在代码中使用ios_base。然而,您不用等到第17章才使用这些方法。
需要说明的最后一点是,每个对象都存储了自己的格式化设置。因此,当程序将cout传递给file_it()时,cout的设置将被修改,然后被恢复;当程序将fout传递给file_it()时,fout的设置将被修改,然后被恢复。