If you want complete safety, you must prevent the user from directly accessing the FILEpointer. Some version of all the normal file I/O functions must show up as class members so that everything you can do with the C approach is available in the C++ class: .
//: C04:Fullwrap.h
// Completely hidden file IO
#ifndef FULLWRAP_H
#define FULLWRAP_H
class File {
std::FILE* f;
std::FILE* F(); // Produces checked pointer to f
public:
File(); // Create object but don't open file
File(const char* path,
const char* mode = "r");
~File();
int open(const char* path,
const char* mode = "r");
int reopen(const char* path,
const char* mode);
int getc();
int ungetc(int c);
int putc(int c);
int puts(const char* s);
char* gets(char* s, int n);
int printf(const char* format, ...);
size_t read(void* ptr, size_t size,
size_t n);
size_t write(const void* ptr,
size_t size, size_t n);
int eof();
int close();
int flush();
int seek(long offset, int whence);
int getpos(fpos_t* pos);
int setpos(const fpos_t* pos);
long tell();
void rewind();
void setbuf(char* buf);
int setvbuf(char* buf, int type, size_t sz);
int error();
void clearErr();
};
#endif // FULLWRAP_H ///:~
This class contains almost all the file I/O functions from . ( vfprintf( )is missing; it is used to implement the printf( )member function.) .
Filehas the same constructor as in the previous example, and it also has a default constructor. The default constructor is important if you want to create an array of Fileobjects or use a Fileobject as a member of another class in which the initialization doesn’t happen in the constructor, but some time after the enclosing object is created .
The default constructor sets the private FILEpointer fto zero. But now, before any reference to f, its value must be checked to ensure it isn’t zero. This is accomplished with F( ), which is privatebecause it is intended to be used only by other member functions. (We don’t want to give the user direct access to the underlying FILEstructure in this class.) [38] The implementation and test files for FULLWRAP are available in the freely distributed source code for this book. See the preface for details.
This approach is not a terrible solution by any means. It’s quite functional, and you could imagine making similar classes for standard (console) I/O and for in-core formatting (reading/writing a piece of memory rather than a file or the console) .
The big stumbling block is the runtime interpreter used for the variable argument list functions. This is the code that parses your format string at runtime and grabs and interprets arguments from the variable argument list. It’s a problem for four reasons .
1.Even if you use only a fraction of the functionality of the interpreter, the whole thing gets loaded into your executable. So if you say printf("%c", 'x');, you’ll get the whole package, including the parts that print floating-point numbers and strings. There’s no standard option for reducing the amount of space used by the program .
2.Because the interpretation happens at runtime, you can’t get rid of a performance overhead. It’s frustrating because all the information is there in the format string at compile time, but it’s not evaluated until runtime. However, if you could parse the arguments in the format string at compile time, you could make direct function calls that have the potential to be much faster than a runtime interpreter (although the printf( )family of functions is usually quite well optimized) .
3.A worse problem is that the format string is not evaluated until runtime: there can be no compile-time error checking. You’re probably familiar with this problem if you’ve tried to find bugs that came from using the wrong number or type of arguments in a printf( )statement. C++ makes a big deal out of compile-time error checking to find errors early and make your life easier. It seems a shame to throw type safety away for an I/O library, especially because I/O is used a lot .
4.For C++, the most crucial problem is that the printf( )family of functions is not particularly extensible. They’re really designed to handle only the four basic data types in C ( char, int, float, double, wchar_t, char*, wchar_t*, and void*)and their variations. You might think that every time you add a new class, you could add overloaded printf( )and scanf( )functions (and their variants for files and strings), but remember, overloaded functions must have different types in their argument lists, and the printf( )family hides its type information in the format string and in the variable argument list. For a language such as C++, whose goal is to be able to easily add new data types, this is an ungainly restriction .
All these issues make it clear that one of the first priorities for the standard class libraries for C++ should handle I/O. Because "hello, world" is the first program just about everyone writes in a new language, and because I/O is part of virtually every program, the I/O library in C++ must be particularly easy to use. It also has the much greater challenge that it must accommodate any new class. Thus, its constraints require that this foundation class library be a truly inspired design. In addition to gaining a great deal of leverage and clarity in your dealings with I/O and formatting, you’ll also see in this chapter how a really powerful C++ library can work .
A stream is an object that transports and formats characters of a fixed width. You can have an input stream (via descendants of the istreamclass), an output stream (with ostreamobjects), or a stream that does both simultaneously (with objects derived from iostream). The iostreams library provides different types of such classes: ifstream, ofstream, and fstreamfor files, and istringstream, ostringstream, and stringstreamfor interfacing with the Standard C++ stringclass. All these stream classes have nearly identical interfaces, so you can use streams in a uniform manner, whether you’re working with a file, standard I/O, a region of memory, or a stringobject. The single interface you learn also works for extensions added to support new classes. Some functions implement your formatting commands, and some functions read and write characters without formatting .
The stream classes mentioned earlier are actually template specializations, [39] Explained in depth in Chapter 5.
much like the standard stringclass is a specialization of the basic_stringtemplate. The basic classes in the iostreams inheritance hierarchy are shown in the following figure .

The ios_baseclass declares everything that is common to all streams, independent of the type of character the stream handles. These declarations are mostly constants and functions to manage them, some of which you’ll see throughout this chapter. The rest of the classes are templates that have the underlying character type as a parameter. The istreamclass, for example, is defined as follows: .
Читать дальше