Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do C++ streams work?

I'd like to know how do stream classes work in C++. When you say:

cout<<"Hello\n";

What does exactly do "<<". I know that cout is an object form iostream that represents the standard output stream oriented to narrow characters (char).

In C "<<" is the bitwise shift operator so it moves bits to the left but in C++ it's and insertion operator. Well, that's all I know, I don't really understand how does this work under the hood.

What I'm asking for is detailed explanation about stream classes in C++, how they are defined and implemented.

Thank you very much for your time and sorry for my English.

like image 395
Mihai Avatar asked Apr 23 '14 19:04

Mihai


People also ask

How does a stream work in programming?

A stream is basically a sequence of data. Whatever data we use in our programming flows through a stream. A stream can be thought of as a channel connecting a processor or logic unit (where data is processed according to the instructions) and input and output devices.

What are input and output streams in C?

We have 3 kinds of I/O streams in C: stdin (standard input) stdout (standard output) stderr (standard error)

What is meant by of stream in C++?

Explanation: ofstream is a stream class to write on files.


3 Answers

Let's create a class that looks like cout (but without as many bells and whistles).

#include <string>

class os_t {
    public:
        os_t & operator<<(std::string const & s) {
            printf("%s", s.c_str());
            return *this;
        }
};

int main() {
    os_t os;

    os << "hello\n";
    os << "chaining " << "works too." << "\n";
}

Notes:

  • operator<< is an operator overload just like operator+ or all of the other operators.
  • Chaining works because we return ourselves: return *this;.

What if you can't change the os_t class because someone else wrote it?

We don't have to use member functions to define this functionality. We can also use free functions. Let's show that as well:

#include <string>

class os_t {
    public:
        os_t & operator<<(std::string const & s) {
            printf("%s", s.c_str());
            return *this;
        }
};

os_t & operator<<(os_t & os, int x) {
    printf("%d", x);
    return os;

    // We could also have used the class's functionality to do this:
    // os << std::to_string(x);
    // return os;
}

int main() {
    os_t os;

    os << "now we can also print integers: " << 3 << "\n";
}

Where else is operator overloading useful?

A great example of how this kind of logic is useful can be found in the GMP library. This library is designed to allow arbitrarily large integers. We do this, by using a custom class. Here's an example of it's use. Note that operator overloading let's us write code that looks almost identical to if we were using the traditional int type.

#include <iostream>
#include <gmpxx.h>

int main() {
    mpz_class x("7612058254738945");
    mpz_class y("9263591128439081");

    x = x + y * y;
    y = x << 2;

    std::cout << x + y << std::endl;
}
like image 129
Bill Lynch Avatar answered Oct 13 '22 13:10

Bill Lynch


<< is a binary operator in C++, and thus can be overloaded.

You know of the C usage of this operator, where 1 << 3 is a binary operation returning 8. You could think of this as the method int operator<<(int, int) where passing in arguments 1 and 3 returns 8.

Technically, operator<< could do anything. It's just an arbitrary method call.

By convention in C++, the << operator is used for handling streams in addition to being the bit-shift operator. When you perform cout << "Hello!", you are calling a method with prototype ostream & operator<< (ostream & output, char const * stream_me). Note the return value ostream &. That return allows you to call the method multiple times, like std::cout << "Hello World" << "!"; which is calling operator<< twice... once on std::cout and "Hello World", and the second time on the result of that first invocation and "!".

In general, if you were to create a class named class Foo, and you wanted it to be printable, you could define your printing method as ostream & operator<< (ostream & output, Foo const & print_me). Here is a simple example.

#include <iostream>

struct Circle {
  float x, y;
  float radius;
};

std::ostream & operator<< (std::ostream & output, Circle const & print_me) {
  output << "A circle at (" << print_me.x << ", " << print_me.y << ") with radius " << print_me.radius << ".";
}

int main (void) {
  Circle my_circle;
  my_circle.x = 5;
  my_circle.y = 10;
  my_circle.radius = 20;

  std::cout << my_circle << '\n';

  return 0;
}
like image 38
QuestionC Avatar answered Oct 13 '22 12:10

QuestionC


Operators can be considered functions with syntactic sugar that allow for clean syntax. This is simply a case of operator overloading.

like image 22
Veritas Avatar answered Oct 13 '22 13:10

Veritas