L13 - String Streams and Operator Overloading
string streams
- String streams are helpful when input are line-oriented, e.g. user will enter a line and program has to check if inputs on that line are correct/expected.
- String streams can be used to extract inputs in the line
syntax
#include <sstream>is the include statement for the classstringstreamis the class name, which must be called when a new string stream object is created<stringstream object name>.str()returns the actual string used by the object, with the cursor at the very beginning of the string- flags:
.eof()is when the end of the string has been reached.fail()is the standard fail flag, as in L12
getline(cin, <string>)reads in everything until a\nis reached, i.e. reads an entire line
notes
- when you call the
stringstreamobject, you create a copy of the string; you do not actually modify the original string directly.
example: reading
#include <sstream> // short for string stream
#include <string>
using namespace std;
int main(void) {
int ID;
string name;
string line = "1001 Joe";
stringstream myStringStream(line);
// after each read, check for .fail() and .eof() flags
myStringStream >> ID; // reads 1001 into ID
myStringStream >> name; // reads "Joe" into name
cout << myStringStream.str() << endl;
}
As good practice, use one
stringstream object for reading, and a separate one for writingOtherwise, you will have multiple cursors.
example: reading entire lines from standard input
getline(cin, <string>);
string command
stringstream myStringStream(command);
string arg1;
myStringStream >> arg1;
// i have no idea what's going on here i'm gonna be honest
operator overloading
- Similar to function overloading, which is when we declare a function (in a class) that has parameters passed to it, and then we declare it again with different parameters (See L06)
- operator overloading is when we accept objects as arguments and use operators on them
- these operators include
+,-,/,*,%,=,+=,!=,==,>=,<,<<,>>, etc.
- these operators include
- essentially, we define what behaviour occurs when we call one of the above operators
Example: the
+ operator
Assuming x and y are objects, then calling:
x + y
is the same as calling:
x.operator+(y)
where operator+ is being invoked on x, and parameter y is passed into the function.
example: fully implementing the operator+ function
When we want to implement the operator+ function on a Complex number (as an example), we have to:
- Pass in a parameter of type
Complex(or whatever object/data type is on the RHS of the equationx + y) - Compute the sum and set a new object of type
Complexequal to that sum - Return a newly created object of type
Complex
Below is the code after it has been implemented
#include <iostream>
using namespace std;
class Complex { // complex number class
private:
double real, img;
public:
Complex() { // first constructor
real = 0;
img = 0;
}
Complex (double r, double i) { // second constructor
real = r;
img = i;
}
operator+(Complex rhs);
}
Complex::operator+(const Complex &rhs) const {
// as good practice, we should use a reference
// instead of a pass by value to save on memory
Complex temp;
temp.real = real + rhs.real;
temp.img = img + rhs.img;
return temp; // returning a COPY of temp (by value)
// we can't return a reference here because the object will no longer exist
// in the memory once this function goes out of scope
}
int main(void) {
Complex x(3,4); // calls second constructor
Complex y(5,6); // calls second constructor
Complex z; // calls first constructor
z = x + y;
}
Notes:
- for the RHS
Complexobject, we can get itsrealcomponent throughtemp.real- however, since we are calling the
operator+function on the LHS object, we can simply directly refer toreal, which gets us the LHS object'srealcomponent
- however, since we are calling the
- we can't return a reference to our new object because it's created on the stack, and will therefore disappear when the function goes out of scope. Thus, we return a copy of the
tempobject we create, which is known as returning by value. - we want to modify neither the RHS nor the LHS objects, so we can use
const:- for RHS, use
constin the pass-by-value or pass-by-reference - for LHS, declare the whole
operator+function withconst
- for RHS, use

To be memory efficient, we can pass an object into the operator function in the class as a reference instead of a pass by value to save on memory
This is assuming we are not making any changes to the passed-in object.
some good terminology to know: pass-by-value and return-by-value
| Value | Reference | |
|---|---|---|
| Pass by: | Creating a copy of a value when we pass it in as a parameter into a function; memory inefficient. | Passing in a reference to a value when we call a function; more memory efficient, but has all the caveats of references. |
| Return by: | Returning a copy of a value in a function. For operator overloading, we always use this because returning by reference is invalid, IF the object that is referenced is created on the stack. | Returning a reference to the value or object in a function. With operator overloading, we do NOT use this because the object we create is created on the stack, and is destroyed once the function goes out of scope. |