. Plab – Tirgul 8 I/O streams Example: string class
Output stream C++ has a Stream I/O Library that supersedes C I/O functions. ostream is the type defined by the library for output streams. cout is an object of type ostream attached to stdout. Examples: #include cout << 7; cout << “Hello World\n”; #include cout << 7; cout << “Hello World\n”;
The ostream overloads the << operator for each basic type (compare to toString() in Java). u The operator returns a reference to the output stream, which allows combined output: Output stream continued cout << “2 + 3 = “ << << endl; u endl is an example of manipulator. endl sends a ‘\n’ character to the stream and flushes it. u In fact endl is a function: << operator has a version that accepts a function pointer u cerr is similar to cout, it is attached to stderr.
Input stream istream is the type defined by the library for input streams. cin is an object of type istream attached to stdin. Examples: #include int i; char a[100]; cin >> i; // reads an int cin >> a; // reads till the 1 st space character #include int i; char a[100]; cin >> i; // reads an int cin >> a; // reads till the 1 st space character Note that >> skips whitespaces.
Input stream continued When an error occurs (typically because the input format is not what we expect) cin enters a failed state and evaluates to false. istream overloads the ! opeator and the void* (conversion) operator For example, to initialize an array we would write: int a[size]; for (int i = 0; i > a[i]; i++) ; int a[size]; for (int i = 0; i > a[i]; i++) ; In failed state istream will produce no input. Use clear() method to continue reading.
More I/O methods Both ostream and istream have additional methods: ostream& put(char ch) ostream& write(char const *str, int length) int get() // read one char istream& get(char& ch) // read one char getline(char *buffer, int size, int delimiter = '\n') Examples: cout.put(‘a’); char ch1, ch2, str[256]; cin.get(ch1).get(ch2); cin.getline(str, 256); cout.put(‘a’); char ch1, ch2, str[256]; cin.get(ch1).get(ch2); cin.getline(str, 256);
File streams To read from files we can use the ifstream, ofstream and fstream. Example: ofstream outFile(“out.txt”); outFile << “Hello world” << endl; int i; ifstream inFile(“in.txt”); while (inFile >> i) {... } int i; ifstream inFile(“in.txt”); while (inFile >> i) {... } Learn the details by yourselves.
. MyString
We would like to have a string class which will replace the inconvenient char*. class MyString { public: MyString(const char* ); MyString(const MyString& ); ~MyString(); int length() const; bool operator==(const MyString& ); private: void init(const char* ); int m_length; char* m_string; }; class MyString { public: MyString(const char* ); MyString(const MyString& ); ~MyString(); int length() const; bool operator==(const MyString& ); private: void init(const char* ); int m_length; char* m_string; }; copy c-tor
MyString.cpp MyString::init(const char* str) { m_string = new char[m_length]; for (int i = 0; i < m_length; i++) m_string[i] = str[i]; } MyString::MyString(const char* str){ m_length = strlen(str); init(str); } MyString::MyString(const MyString& str) { m_length = str.m_length; init(str.m_string); } MyString::init(const char* str) { m_string = new char[m_length]; for (int i = 0; i < m_length; i++) m_string[i] = str[i]; } MyString::MyString(const char* str){ m_length = strlen(str); init(str); } MyString::MyString(const MyString& str) { m_length = str.m_length; init(str.m_string); }
MyString.cpp continued int MyString::length() const { return m_length; } bool MyString::operator==(const MyString& str){ if (str.m_length != m_length) return false; int i; for (i = 0; m_string[i] == str.m_string[i] && i < m_length; i++) ; return (i == m_length); } MyString::~MyString() { if (m_string) delete [] m_string; } int MyString::length() const { return m_length; } bool MyString::operator==(const MyString& str){ if (str.m_length != m_length) return false; int i; for (i = 0; m_string[i] == str.m_string[i] && i < m_length; i++) ; return (i == m_length); } MyString::~MyString() { if (m_string) delete [] m_string; }
Using our string u Examples of using the string class: MyString a = “12345”; MyString b = a; MyString c = “6879”; if (a == c) {... } if (a == b) {... } MyString a = “12345”; MyString b = a; MyString c = “6879”; if (a == c) {... } if (a == b) {... } u But what about something like this? if (a == “123”) {... } It also works, by constructing MyString object from “123”, and using the operator== already defined.
Adding == u But what about something like this? if (“123” == a) {... } It is still undefined (and won’t compile). To be able to compare C-style string with MyString we have to define a friend function: friend bool operator==(const char*, MyString&); bool operator== (const char* s1, MyString& s2) { // compare s1 with s2.m_string //... } inside MyString.h inside MyString.cpp
= operator Since up to now we haven’t defined = operator, the default assignment operator is used. It performs a shallow copy, which is obviously not what we want. If we want our strings to be immutable we should define the = operator as private (in.h file): private: MyString& operator=(const MyString& ); MyString& MyString::operator=(const MyString& s) { assert(false); } u It would be implemented like this:
= operator continued u Otherwise the operator is public public: MyString& operator=(const MyString& ); MyString& MyString::operator=(const MyString& s) { // delete m_string, copy s.m_string to m_string //... return *this; } MyString& MyString::operator=(const MyString& s) { // delete m_string, copy s.m_string to m_string //... return *this; } u It would be implemented like this: u Returning a reference to the object itself is essential for statements like: s1 = s2 = s3;
Swapping objects After we have a well defined operator= we can write a swap function for MyString. void swap(MyString& s1, MyString& s2) { MyString temp = s1; s1 = s2; s2 = temp; } void swap(MyString& s1, MyString& s2) { MyString temp = s1; s1 = s2; s2 = temp; } u Compare to: (both in C++ and in Java) void swap(MyString s1, MyString s2) { MyString temp = s1; s1 = s2; s2 = temp; } void swap(MyString s1, MyString s2) { MyString temp = s1; s1 = s2; s2 = temp; }
operator[] u We can use [] to access individual characters in the string. char& MyString::operator[](int i) { return m_string[i]; } char& MyString::operator[](int i) { return m_string[i]; } u Examples of use: MyString a = “12345”; if (a[2] == ‘3’) {... } a[4] = ‘7’; MyString a = “12345”; if (a[2] == ‘3’) {... } a[4] = ‘7’; u Unfortunately it also allows: It will result in segfault in a ’s destructor (if not before). Why? delete [] &a[0];
Additional operators u Here are some additional operators we may want in our string class: != + (to concatenate strings) += < and <= > and >= etc... In fact a string class is part of C++ standard library, it has all the features of MyString and much more…
I/O of MyString One of the advantages of using streams is the ability to define I/O operators for our own types. friend ostream& operator<<(ostream&, const MyString&); ostream& operator<<(ostream& os, const MyString& str) { os.write(str.m_string[i], str.m_length); return os; } Example: MyString a = "12345", b = "6789"; cout << a << “ “ << b << endl;
I/O of MyString continued friend istream& operator>>(istream&, MyString&); istream& operator>>(istream& is, MyString& str) { // read until whitespace, // extend str.m_string if required return is; } istream& operator>>(istream& is, MyString& str) { // read until whitespace, // extend str.m_string if required return is; } Example: MyString a = “”, b = “”; cin >> a >> b;