220 likes | 410 Vues
Disk I/O Input and output to text files are handled by including the file "fstream.h" and by then declaring variables of type ifstream, and ofstream respectively . #include <fstream.h> // for ifstream, ofstream classes #include <iostream.h> int main() { int n1, n2, n3 ;
E N D
Disk I/O Input and output to text files are handled by including the file "fstream.h" and by then declaring variables of type ifstream, and ofstream respectively. #include <fstream.h> // for ifstream, ofstream classes #include <iostream.h> int main() { int n1, n2, n3 ; ifstream inFile = "in.dat.txt" ; inFile >> n1 >> n2 >> n3 ; cout << "n1 = " << n1 << endl; cout << "n2 = " << n2 << endl; cout << "n3 = " << n3 << endl; //--------- writing data to a file (no spaces !!!) ofstream outFile("out.dat"); outFile << n1 << n2 << n3 ; //-------- reading data until end of file inFile.close(); inFile.open("in.dat.txt"); int num; while (!inFile.eof()) { inFile >> num; cout << num << endl; }
// ------ reading data from keyboard • cout << endl; • int sum, x; • sum = 0; • cout << "Please enter numbers followed by Enter" << endl; • cout << "Hit Ctrl-Z (DOS) Ctrl-D (unix) when done " << endl ; • while ((!cin.eof()) && cin >> x) • sum = sum + x ; • cout << endl; • cout << endl <<"sum of numbers entered = " << sum << endl; • return 0; • } • The following program reads from a file named "xxx" and writes to a file named "yyy": • #include <iostream. h> • #include <fstream.h> • void main() • { • char c; • ifstream infile("xxx'); ofstream outfile("yyy"); • if (outfile && infile) // They will be 0 on err. • while (infile >> c) • outfile << c; • } • The above code does not work as expected, because blanks, tabs, and '\n' characters at the end of each line are ignored as white space when using << on a character.
Instead, the "get" function can be used to correct the above problem, as shown below: • while (infile.get (c)) • outfile << c; • or: • while (infile.get(c)) • outfile.put(c); • It is also possible to read complete lines by calling the "getline" function in the same manner as used for the "get" function. • To open a file for appending, use the following: • ofstream ( "xxx", iosapp); • Note that no "close" function is needed for file input and output. A file automatically closes itself when the file variable goes out of scope. If you do need to explicitly close a file, you can say: • outfile close(); • #include <fstream.h> • #include <assert.h> • . . . . • ifstream infile("raw.dat"); • assert(infile); // use assertion to see if successful • . . . . • ofstream outfile("process.dat"); • assert(outfile); • C systems have stdin, stdout, and stderr as standard files. In addition, systems may define other standard files, such as stdprn and stdaux. Abstractly a file may be thought of as a stream of characters that are processed sequentially.
Written in C Name Remark • stdin standard input file connected to the keyboard • stdout standard output file connected to the screen • stderr standard error file connected to the screen • stdprn standard printer file connected to the printer • stdaux standard auxiliary file connected to an auxiliary port • The C++ stream input/output ties the first three of these standard files to cin, cout, and cerr, respectively. Typically C++ ties cprn and caux to their corresponding standard files stdprn and stdaux. There is also clog, which is a buffered version of cerr. Other files can be opened or created by the programmer. • File I/O is handled by including fstream.h. This contains classes ofstream and ifstream for output file stream and input file stream creation and manipulation. To properly open and manage an ifstream or ofstream related to a system file, you first declare it with an appropriate constructor. First we study the ifstream behavior. • ifstream(); • ifstream(const char*, int= ios::in, int prot=filebuf::openprot); • The constructor of no arguments creates a variable that later will be associated with an input file. The constructor of three arguments takes as its first argument the named file. The second argument specifies the file mode. The third argument is for file protection. • The file mode arguments are defined as enumerators in class ios as follows: • ios::in //input mode • los::app //append mode • ios::out //output mode • ios::ate //open and seek to end of file • ios::nocreate //open but do not create mode • ios::trunc //discard contents and open • ios::noreplace //if file exists open fails • Thus the default for an ifstream is input mode and for an ofstream is output mode.
ios istream ostream ifstream ofstream • Some classes in the Inheritance hierarchy for stream I/O • Class Description Header File • ios stream I/0 base class iostream.histream input stream class iostream.hostream output stream class iostream.hifstream input file access fstream.h (includes iostream.h)ofstream output file access fstream.h (includes iostream.h) • Formatted Output • The header file iomanip.h contains several format manipulators that affect the appearance of the output. Thus, to use these manipulators, we must include the header file, as follows: • #include <iomanip.h> • The manipulator setprecision(n) sets the floating point precision and applies to all subsequent float output. For example, the following segment using setprecision(2) displays each number rounded to two decimal places.
#include <iostream.h> • #include <iomanip.h> • . . . . • float x = 93.452, y = 2.35729,; z = 13.50; • cout << setprecision(2) << "x = " << x << endl; • cout << "y = " << y << endl; • cout << "z = " << z << endl; • The output of above program is: • x = 93.45 y = 2.36 z = 13.5 • The manipulator setw(m) sets the field width to m for the next output only. To display x and y in a field of width 10 with two places after the decimal, such as • : 93.45: 2.36: • we write the following segment: • cout << ":" << setprecision(2) << setw(10) << x << ":" • << setw(10) << y << ":" << endl; • Suppose, however, we have the following declaration initializations: • float x = 1112.3,y = 123.458; • The output could appear with exponential notation, as follows: • : 1.lle+03: 1.23e+02: • To ensure that subsequent float output is in fixed point instead of exponential notation, we set the flag fixed with the manipulator setiosflags, as follows:
#include <iomanip.h> • …….. • cout << setiosflags(ios: :fixed) • << ":" << setprecision(2) << setw(10) << x << ":" • << setw(10) << y << ":" << endl; • The resulting output is • : 5112.3: 123.46: • A flag indicates one of two possible situations--in this case, that the output be in fixed instead of exponential notation. • If the values for x and y indicate an amount in dollars and cents, then we always need to display two decimal places, such as 1112.30. Another flag, showpoint, ensures • that both decimal places are printed. We can set both flags--showpoint and fixed--using, such as setiosflags(ios::showpoint | ios::fixed). Thus, for x being 1112.3 and y being 123.458, to display $1112.30 and $123.46 • we write • cout << setiosflags(ios::showpoint|ios::fixed) << setprecision(2) << "$" << x << " and " << "$" << y << endl; • To reverse the setting of these flags, we use resetiosfiags, such as follows: • cout << resetiosflags(ios::showlooint|ios::fixed); • By default, an output item is right-justified in its field, or written on the far right of the output field. To override this default, we use cout.setf with the flag adjustfield and the value left, such as • cout.setf(ios::left, ios::adjustfield); • Thus, to display the name "Anderson, J. K." left-justified and a number 4334.56 right-justified on the same line with colons separating fields, such as • :Anderson, J. K. : 4334.56:
we write as: • cout.setf(ios::left, ios::adjustfield); • cout << ":" << setw(18) << "Anderson, J. K."; cout.setf(ios::right, ios::adjustfield); • cout << ":" << setw(10) << 4334.56 << ":" << endl; • Summary of manipulators: • setprecision(n): set the floating point precision for at most n digits after the decimal for all subsequent float output • setw(m): set the field width to m for the next output only • setiosflags( ios::fixed): set subsequent float output to be in fixed point • setiosflags(ios::showpoint|ios::fixed): set subsequent float output to be in fixed point notation, showing all decimal places indicated by the precision • resetiosflags(ios::showpoint|ios::fixed): reverse the setting of the showpoint and fixed flags • cout.setf ( ios::left, ios::adjustfield) : set formatting flag to left-justify subsequent output • cout.setf( ios::right, ios::adjustfield) ;: set formatting flag to right-justify subsequent output • Printing Addresses: • The cout mechanism automatically understands addresses and formats them for hex output, for example, if i is an integer, the statement: • cout << &i <<endl; • prints the address of i in hex format. If p is a pointer to i, printing p also prints the address of i in hex format. There are cases, however, where this formatting rule does not hold. Printing s, where s is a pointer to a character, produces the string pointed to by s rather than the address held by s. To remedy this situation, cast s to a void pointer as shown below if you want to see its address: • cout << (void *) s;
Now the address held by s will be shown in hex format. If you wish to display an address as a decimal number rather than in hex format, cast it to a long integer: • cout << long(&i); • This line prints the address of i in decimal format. In the same way, an int cast • is used to print out the integer value of a character: • cout << int('A');// produces 65 as output • You may notice that the << operator - known in C as the shift left operator, has been stolen to handle output in C++. If you wish to use it for shifting left within an output line, then parentheses should be used: • cout << (2 << 4);// produces 32 as output • To format output, you can use several techniques. Information can be spaced by adding in spaces or tabs as literal strings, as shown below: • int i=2; float f=3.14;char c='A'; char *s = "hello"; • cout << s << << c << "\t" << f • << "\t"<< i << endl; • There are several other manipulators that can be inserted into an output stream (on many systems you will have to include "iomanip.h" to use these)' • dec Use decimal base • oct Use octal base • hex Use hex base • endl End of line • ends End of string ('\0') • flush Flush output buffer • setw(w) Set output width to w (0 is default) • setfill(c) Set fill character to c (blank is default)
setprecision(p) Set float precision to p • cout << "[" << setw(6) << setfill(‘*’) << 192; • cout << "]" << endl; • cout << hex << "[" << setw(6); • cout << setfill(‘*’) << 192 << "]" << endl; • cout << setprecision(4) << 3.14159 << endl; • produces: • [***192] • [****c0] • 3.142 • String I/O • Input can be read from strings in memory, and output can be sent to strings in memory, duplicating the action of sscanf and sprintf. To do this, you must include the file "strstrea.h" and then declare input and output strings. • char s[lOO]; • ostrstream outstring(s,lO0); • outstring << 3.14 << " is pi" << ends; • cout << s; • The string s is filled with the text "3.14 is pi". If s is overfilled, outstring will automatically stop placing values into it. • If a string s exists and you wish to read from it, you can use an input string stream as shown below: • char *s = "3.1412 cat"; • istrstream instring(s, strlen(s)); • float f; int i; • char t[lO0]; • instring >> f >> i >> t;
P_buy p_sell VTABLE Pointer Code for profit_loss Table of function pointer • Inheritance in C++ • //Manipulating transactions using inheritance • #include <lostream.h> • struct transact { //base class for stock and callchar security[20];int p_buy;int p_sell;virtual int profit_loss () {return (p_sell-p_buy); } • }; • //derived classes • class stock : public transact { • public: • int dividend; • int profit_loss() { return (dividend + p_sell-p_buy);} • }; • class call : public transact { • public: • int strike_price; • int month; • }; By default, the member of struct is public
main () { transact * t[2]; //pointers to the base class stock s; call c; s.p_buy = 10; s.p_sell = 11; s.dividend = 2; c.p_buy = 10; c.p_sell = 11; t[O] = &s; //a base class pointer can t[1] = &c; //point at a derived class cout << t[0] -> profit loss () << "\n"; cout << t[1] -> profit loss () << "\n"; } Understanding Virtual Functions #include <iostream.h> void f1( ); main( ) { ….. ….. f1( ) ; // f1 invoked …. …. f1( ) ; // f1 invoked again … } Dividend + p_sell - p_buy p_sell-p_buy - p_buy
void f1 ( ) { ….. cout << “f1 called “ << endl; …. …. } #include <iostream.h> class x { public: int a, b; void f1( ); }; void x::f1( ) { ….. cout << “f1 of class x called “ << endl; …. …. } main( ) { x x1, x2; ….. ….. x1.f1( ) ; X2.f1( ) ; … } Executable code for f1() of class x x1.a x1.b x2.a x2.b call routine @300 call routine @300 300 400 401 402 403 Object x1 After compilation Object x2 500 Executable code for main()
#include <iostream.h> class x { public: int a, b; void f1( ); }; void x::f1( ) { ….. cout << “f1 of class x called “ << endl; …. …. } class y : public x { public: int c; void f1( ); }; void y::f1( ) { ….. cout << “f1 of class y called “ << endl; …. …. }
main( ) { x x1, x2; y y1; ….. ….. x1.f1( ) ; x2.f1( ) ; …. …. x * bp; bp = & y1; bp -> f1( ) ; } #include <iostream.h> class x { public: int a, b; virtual void f1( ); }; void x::f1( ) { ….. cout << “f1 of class x called “ << endl; …. …. } class y : public x { public: int c; void f1( ); }; When we use base class pointer to access a derived class object, and we are calling a member function that has been overridden in the derived class, this function must be declared as VIRTURAL in the base class, in this example, bp->f1() will invoke the member function of class x.
void y::f1( ) { • ….. • cout << “f1 of class y called “ << endl; • …. • …. • } • main( ) { • x x1, x2; • y y1; • ….. • ….. • x1.f1( ) ; • x2.f1( ) ; • …. • …. • x * bp; • bp = & y1; • bp -> f1( ) ; • } • class CShape • { • public: • CShape(); • double GetArea(); • void GetCenter( int* x, int* y ); void SetCenter( int* x, int* y ); void Draw();protected: int m_xCoord; int m_yCoord; }; After declaring f1 as virtual function in base class x, bp->f1() will invoke the member function of class y.
class CSquare : public CShape { • public: • CSquare(); void Draw(); double GetArea(); double GetSideLength(); • private: • double m_dLength; }; • class CCircle : public CShape { • public: • CCircle(); • void Draw(); • double GetArea(); • double GetRadius(); • private: • double m_dRadius; }; • An example of calling the wrong function through a base-class pointer. • #include <iostream.h> • // Most of the member data and functions have been removed for simplicity • class CShape { • public: • void Draw(); • }; • void CShape::Draw() • { cout << "CShape::Draw called" << endl; } • } • class CCircle : public CShape { • public: • void Draw(); • }; • void CCircle::Draw() • { cout << "Circle::Draw called" << endl; } • //
int main() • { • CShape* pShape = new CCircle; • pShape->Draw(); • delete pShape; • return 0; } • Note that the virtual keyword is only used in the class declaration, not in the function definition. • Using Pure Virtual Functions • When a base class declares a virtual function, it sometimes makes no sense for the base class to provide any implementation for the function. A good example of this is shown in the CShape class. The CShape base class cannot provide a meaningful Draw function, because it has no way of knowing how a particular shape should be drawn. • Thus the CShape class should be an abstract class. Abstract classes are allowed to declare functions that are not defined in the base class. All subclasses of CShape are expected to provide a Draw member function. To force all subclasses of a class to implement a virtual function, you can declare that function as a "pure" virtual function by adding = 0; to its declaration. • If a class has at least one pure virtual function, that class becomes an abstractbase class, and it can never be instantiated. All its subclasses MUST provide their implementation of the virtual function otherwise it will cause a compile error.
Using a pure virtual function in a base class. • #include <iostream.h> • // Most of the member data and functions have been removed for simplicity • class Cshape { • public: • virtual void Draw() = 0; // pure virtual function • }; • ciass CCircle : public Cshape { • public: • virtual void Draw(); • }; • void CCircle::Draw() • { cout << "Circle::Draw called" << endl; } • // • int main() • { • CShape* pShape = new CCircle; • pShape->Draw(); • delete pShape; • return 0; } • The following code generates a compiler error: CShape aShape; ?? • Using Virtual Destructors • When you use pointers to base classes to access objects derived from the base class, a serious problem can occur when the object is destroyed. It's possible for the wrong destructor to be called, resulting in a partially destroyed object. • #include <iostream.h> Pure virtual function no code exists here, this class is an abstract class the derived class must override it and provide the code
// Most of the member data and functions have been removed for simplicity • class CShape { • public: • ~CShape(); • virtual void Draw() = 0; // pure virtual function • }; • CShape::~CShape() { cout << “Base desctructor called “ << endl; } • class CCircle : public CShape { • public: • virtual ~CCircle(); • virtual void Draw(); • }; • CCircle::~CCircle() { cout << “CCircle desctructor called “ << endl; } • void CCircle::Draw() • { cout << "Circle::Draw called" << endl; } • // • int main() • { • CShape* pShape = new CCircle; • pShape->Draw(); • delete pShape; • return 0; }
The corrected code: • #include <iostream.h> • // Most of the member data and functions have been removed for simplicity • class CShape { • public: • virtual ~CShape(); • virtual void Draw() = 0; // pure virtual function • }; • CShape::~CShape() { cout << “Base desctructor called “ << endl; } • class CCircle : public CShape { • public: • virtual ~CCircle(); • virtual void Draw(); • }; • CCircle::~CCircle() { cout << “CCircle desctructor called “ << endl; } • void CCircle::Draw() • { cout << "Circle::Draw called" << endl; } • // • int main() • { • CShape* pShape = new CCircle; • pShape->Draw(); • delete pShape; • return 0; }