Team LiB
Previous Section Next Section

8.1. An Object-Oriented Library

8.1. 面向对象的标准库

The IO types and objects we've used so far read and write streams of data and are used to interact with a user's console window. Of course, real programs cannot be limited to doing IO solely to or from a console window. Programs often need to read or write named files. Moreover, it can be quite convenient to use the IO operations to format data in memory, thereby avoiding the complexity and run-time expense of reading or writing to a disk or other device. Applications also may have to read and write languages that require wide-character support.

迄今为止,我们已经使用 IO 类型和对象读写数据流,它们常用于与用户控制窗口的交互。当然,实际的程序不能仅限于对控制窗口的 IO,通常还需要读或写已命名的文件。此外,程序还应该能方便地使用 IO 操作格式化内存中的数据,从而避免读写磁盘或其他设备的复杂性和运行代价。应用程序还需要支持宽字符(wide-character)语言的读写。

Conceptually, neither the kind of device nor the character size affect the IO operations we want to perform. For example, we'd like to use >> to read data regardless of whether we're reading a console window, a disk file, or an in-memory string. Similarly, we'd like to use that operator regardless of whether the characters we read fit in a char or require the wchar_t (Section 2.1.1, p. 34) type.

从概念上看,无论是设备的类型还是字符的大小,都不影响需要执行的 IO 操作。例如,不管我们是从控制窗口、磁盘文件或内存中的字符串读入数据,都可使用 >> 操作符。相似地,无论我们读的是 char 类型的字符还是 wchar_t第 2.1.1 节)的字符,也都可以使用该操作符。

At first glance, the complexities involved in supporting or using these different kinds of devices and different sized character streams might seem a daunting problem. To manage the complexity, the library uses inheritance to define a set of object-oriented classes. We'll have more to say about inheritance and object-oriented programming in Part IV, but generally speaking, types related by inheritance share a common interface. When one class inherits from another, we (usually) can use the same operations on both classes. More specifically, when two types are related by inheritance, we say that one class "inherits" the behaviorthe interfaceof its parent. In C++ we speak of the parent as the base class and the inheriting class as a derived class.

乍看起来,要同时支持或使用不同类型设备以及不同大小的字符流,其复杂程度似乎相当可怕。为了管理这样的复杂性,标准库使用了继承(inheritance)来定义一组面向对象(object-oriented)类。在本书的第四部分将会更详细地讨论继承和面向对象程序设计,不过,一般而言,通过继承关联起来的类型都共享共同的接口。当一个类继承另一个类时,这两个类通常可以使用相同的操作。更确切地说,如果两种类型存在继承关系,则可以说一个类“继承”了其父类的行为——接口。C++ 中所提及的父类称为基类(base class),而继承而来的类则称为派生类(derived class)

The IO types are defined in three separate headers: iostream defines the types used to read and write to a console window, fstream defines the types used to read and write named files, and sstream defines the types used to read and write in-memory strings. Each of the types in fstream and sstream is derived from a corresponding type defined in the iostream header. Table 8.1 lists the IO classes and Figure 8.1 on the next page illustrates the inheritance relationships among these types. Inheritance is usually illustrated similarly to how a family tree is displayed. The topmost circle represents a base (or parent) class. Lines connect a base class to its derived (or children) class(es). So, for example, this figure indicates that istream is the base class of ifstream and istringstream. It is also the base class for iostream, which in turn is the base class for sstream and fstream classes.

IO 类型在三个独立的头文件中定义:iostream 定义读写控制窗口的类型,fstream 定义读写已命名文件的类型,而 sstream 所定义的类型则用于读写存储在内存中的 string 对象。在 fstreamsstream 里定义的每种类型都是从 iostream 头文件中定义的相关类型派生而来。表 8.1 列出了 C++ 的 IO 类,而图 8.1 则阐明这些类型之间的继承关系。继承关系通常可以用类似于家庭树的图解说明。最顶端的圆圈代表基类(或称“父类”),基类和派生类(或称“子类”)之间用线段连接。因此,图 8.1 所示,istreamifstreamistringstream 的基类,同时也是 iostream 的基类,而 iostream 则是 stringstreamfstream 的基类。

Table 8.1. IO Library Types and Headers
表 8.1. IO 标准库类型和头文件

Header

Type

iostream

istream reads from a stream

istream 从流中读取

ostream writes to a stream

ostream 写到流中去

iostream reads and writes a stream; derived from istream and ostream,

iostream 对流进行读写;从 istreamostream 派生而来

fstream

ifstream, reads from a file; derived from istream

ifstream 从文件中读取;由 istream 派生而来

ofstream writes to a file; derived from ostream

ofstream 写到文件中去;由 ostream 派生而来

fstream, reads and writes a file; derived from iostream

fstream 读写文件;由 iostream 派生而来

sstream

istringstream reads from a string; derived from istream

istringstreamstring 对象中读取;由 istream 派生而来

ostringstream writes to a string; derived from ostream

ostringstream 写到 string 对象中去;由 ostream 派生而来

stringstream reads and writes a string; derived from iostream

stringstreamstring 对象进行读写;由 iostream 派生而来


Figure 8.1. Simplified iostream Inheritance Hierarchy
图 8.1. 简单的 iostream 继承层次


Because the types ifstream and istringstream inherit from istream, we already know a great deal about how to use these types. Each program we've written that read an istream could be used to read a file (using the ifstream type) or a string (using the istringstream type). Similarly, programs that did output could use an ofstream or ostringstream instead of ostream. In addition to the istream and ostream types, the iostream header also defines the iostream type. Although our programs have not used this type, we actually know a good bit about how to use an iostream. The iostream type is derived from both istream and ostream. Being derived from both types means that an iostream object shares the interface of both its parent types. That is, we can use an iostream type to do both input and output to the same stream. The library also defines two types that inherit from iostream. These types can be used to read or write to a file or a string.

由于 ifstreamistringstream 类型继承了 istream 类,因此已知这两种类型的大量用法。我们曾经编写过的读 istream 对象的程序也可用于读文件(使用 ifstream 类型)或者 string 对象(使用 istringstream 类型)。类似地,提供输出功能的程序同样可用 ofstreamostringstream 取代 ostream 类型实现。除了 istreamostream 类型之外,iostream 头文件还定义了 iostream 类型。尽管我们的程序还没用过这种类型,但事实上可以多了解一些关于 iostream 的用法。iostream 类型由 istreamostream 两者派生而来。这意味着 iostream 对象共享了它的两个父类的接口。也就是说,可使用 iostream 类型在同一个流上实现输入和输出操作。标准库还定义了另外两个继承 iostream 的类型。这些类型可用于读写文件或 string 对象。

Using inheritance for the IO types has another important implication: As we'll see in Chapter 15, when we have a function that takes a reference to a base-class type, we can pass an object of a derived type to that function. This fact means that a function written to operate on istream& can be called with an ifstream or istringstream object. Similarly, a function that takes an ostream& can be called with an ofstream or ostringstream object. Because the IO types are related by inheritance, we can write one function and apply it to all three kinds of streams: console, disk files, or string streams.

对 IO 类型使用继承还有另外一个重要的含义:正如在第十五章可以看到的,如果函数有基类类型的引用形参时,可以给函数传递其派生类型的对象。这就意味着:对 istream& 进行操作的函数,也可使用 ifstream 或者 istringstream 对象来调用。类似地,形参为 ostream& 类型的函数也可用 ofstream 或者 ostringstream 对象调用。因为 IO 类型通过继承关联,所以可以只编写一个函数,而将它应用到三种类型的流上:控制台、磁盘文件或者字符串流(string streams)

International Character Support

国际字符的支持

The stream classes described thus far read and write streams composed of type char. The library defines a corresponding set of types supporting the wchar_t type. Each class is distinguished from its char counterpart by a "w" prefix. Thus, the types wostream, wistream, and wiostream read and write wchar_t data to or from a console window. The file input and output classes are wifstream, wofstream, and wfstream. The wchar_t versions of string stream input and output are wistringstream, wostringstream, and wstringstream. The library also defines objects to read and write wide characters from the standard input and standard output. These objects are distinguished from the char counterparts by a "w" prefix: The wchar_t standard input object is named wcin; standard output is wcout; and standard error is wcerr.

迄今为止,所描述的流类(stream class)读写的是由 char 类型组成的流。此外,标准库还定义了一组相关的类型,支持 wchar_t 类型。每个类都加上“w”前缀,以此与 char 类型的版本区分开来。于是,wostreamwistreamwiostream 类型从控制窗口读写 wchar_t 数据。相应的文件输入输出类是 wifstreamwofstreamwfstream。而 wchar_t 版本的 string 输入/输出流则是 wistringstreamwostringstreamwstringstream。标准库还定义了从标准输入输出读写宽字符的对象。这些对象加上“w”前缀,以此与 char 类型版本区分:wchar_t 类型的标准输入对象是 wcin;标准输出是 wcout;而标准错误则是 wcerr

Each of the IO headers defines both the char and wchar_t classes and standard input/output objects. The stream-based wchar_t classes and objects are defined in iostream, the wide character file stream types in fstream, and the wide character stringstreams in sstream.

每一个 IO 头文件都定义了 charwchar_t 类型的类和标准输入/输出对象。基于流的 wchar_t 类型的类和对象在 iostream 中定义,宽字符文件流类型在 fstream 中定义,而宽字符 stringstream 则在 sstream 头文件中定义。

No Copy or Assign for IO Objects

IO 对象不可复制或赋值

For reasons that will be more apparent when we study classes and inheritance in Parts III and IV, the library types do not allow allow copy or assignment:

出于某些原因,标准库类型不允许做复制或赋值操作。其原因将在后面第三部分第四部分学习类和继承时阐明。

    ofstream out1, out2;
    out1 = out2;   // error: cannot assign stream objects
    // print function: parameter is copied
    ofstream print(ofstream);
    out2 = print(out2);  // error: cannot copy stream objects

This requirement has two particularly important implications. As we'll see in Chapter 9, only element types that support copy can be stored in vectors or other container types. Because we cannot copy stream objects, we cannot have a vector (or other container) that holds stream objects.

这个要求有两层特别重要的含义。正如在第九章看到的,只有支持复制的元素类型可以存储在 vector 或其他容器类型里。由于流对象不能复制,因此不能存储在 vector(或其他)容器中(即不存在存储流对象的 vector 或其他容器)。

The second implication is that we cannot have a parameter or return type that is one of the stream types. If we need to pass or return an IO object, it must be passed or returned as a pointer or reference:

第二个含义是:形参或返回类型也不能为流类型。如果需要传递或返回 IO 对象,则必须传递或返回指向该对象的指针或引用:

    ofstream &print(ofstream&);              // ok: takes a reference, no copy
    while (print(out2)) { /* ... */ } // ok: pass reference to out2

Typically, we pass a stream as a nonconst reference because we pass an IO object intending to read from it or write to it. Reading or writing an IO object changes its state, so the reference must be nonconst.

一般情况下,如果要传递 IO 对象以便对它进行读写,可用非 const 引用的方式传递这个流对象。对 IO 对象的读写会改变它的状态,因此引用必须是非 const 的。

Exercises Section 8.1

Exercise 8.1:

Assuming os is an ofstream, what does the following program do?

假设 os 是一个 ofstream 对象,下面程序做了什么?

    os << "Goodbye!" << endl;

What if os is an ostringstream? Whatif os is an ifstream?

如果 osostringstream 对象呢?或者,osifstream 呢?

Exercise 8.2:

The following declaration is in error. Identify and correct the problem(s):

下面的声明是错误的,指出其错误并改正之:

    ostream print(ostream os);


Team LiB
Previous Section Next Section