14

Possible Duplicate:
Operator overloading

I'm making a long awaited return to C++ and there's some basic notation that doesn't really seem to be that prominent in other languages.

If you look at this line of code

cout << "firstvalue is " << firstvalue << endl;

I realise what this does. It write's "firstvalue is x" to the console. x being the value of firstvalue. However, I do not know anything about the "<<" or ">>" double angled brackets. I haven't been able to research them or what they do as I don't know the formal name for them.

My question is, what actually happens (step by step) in the above statement? And what are these "<<" for? I think I understand that cout is a standard library function for writing to the console. However I'm used to either objective-c or dot notation. I do not see what object this "cout" function is a member of.

I can understand printf a little more easily, as at least it provides braces for the arguments. e.g. printf("your string here").

4

5 回答 5

22

C++ allows operator overloading. That means a user-defined type can define its own behavior on built-in operators. In this case the operators are called: left shift or right shift operators. Those operators have been traditionally been used for bit-shifting, but the standard library repurposes them to symbolize streaming operations.

You can find a list of the available operators in C and C++ here.

In your case you are streaming a string literal and a value of some type into std::cout, which is an object of type std::basic_ostream.

Step-by-Step

After precedence rules have been applied your code looks like this:

((cout << "foobar") << x) << endl;

The compiler will basically transform the object << object expressions into function calls.

operator<<(operator<<(operator<<(cout, "foobar"), x), endl);

Then it will figure out which overload to call. (This is really tricky. For now it should be sufficient to believe that it simply looks for an overload of operator<< with matching arguments).

Most of the built-in overloads for basic_ostream are here and here.

于 2012-10-17T16:29:50.310 回答
5

The << operator is the "arithmetic left shift" in C++. For example:

3 << 2

evaluates to 12. The reason is that the binary representation of 3 is

00000011

shifting it twice to the left you get

00001100

and the numeric value of the result is 12.

What it has to do this with output? Nothing at all, actually. However in C++ you can redefine the meaning of operators thanks to overloading. The C++ standard library decided to redefine the meaning of the left-shift operator as sort of a "send-to-stream".

So what happens is that

std::cout << "whatever"

returns as value std::cout, but as side effect it outputs the string "whatever".

The operator was chosen because it had a reasonable precedence (overloading cannot change precedence, and you cannot define new operators) and the shape makes it appear somewhat "natural". Note however that the left-shift operator is just a normal operator and for example there is no guarantee about order of evaluation:

std::cout << f() << g() << h();

here the output will be the result of calling f(), followed by the result of calling g() and followed by the result of calling h()... but the functions themselves may be are called in a different order and for example h() could be called first!

So in a sense the "sequence look" of the operator is misleading, because it's about the sequence of output, but not about the sequence of evaluation.

于 2012-10-17T16:37:48.100 回答
2

They're referred to as stream insertion (or extraction, in the case of istream >>), and are actually a semantic overload of the left-shift and right-shift operators.

So, this:

int x = 1 << 1;

is a bit shift, but this:

std::cout << x;

is a stream insertion. You can write it out explicitly as:

operator <<(std::cout, x);

and get exactly the same result. The conventional format of stream insertion operators (they can be overloaded for user-defined types, so it's not unusual to write your own) is

std::ostream& operator <<(std::ostream&, T value);

The output stream is returned (by reference) so you can chain calls: your example translates as:

operator<< (
  operator<< (
    operator<<(std::cout, "firstvalue"),
    firstvalue
  ),
  std::endl
);

Oh, and ... std::cout (and std::cerr etc.) are not functions: they're global objects. The function here is the overloaded << operator. Think of them as FILE *stdout, *stderr equivalents.

There are some advantages of C++ iostreams over printf et. al:

  • type-safety: you can't mistakenly print an integer with "%f" and get garbage, because overload resolution automatically selects the std::ostream& operator<<(std::ostream&, double) function at compile time
  • support for user-defined types: you can write a stream insertion operator for your whizzy new class, and it will just work, everywhere
  • stream abstraction: you can use the same overloads (so you only write them once) to format to stdout and stderr (cout/cerr), and files (std::ofstream) and strings (std::ostringstream). There's no need to handle printf/fprintf/snprintf separately.

There are also some disadvantages:

  • performance: there is some penalty to all that abstraction, and the generality of the locale system which is configured at runtime
  • verbosity: at least for primitive types already supported by printf, the format strings are much terser and more expressive
于 2012-10-17T16:35:05.183 回答
2

The << is an operator, the same way + is an operator and * is an operator. In the way the following are equivalent expressions:

5 + 3 + 2
((5 + 3) + 2)

So are the next two:

std::cout << "Hello" << std::endl
((std::cout << "Hello") << std::endl)

It's just an operator with two operands. For the fundamental types, the << and >> operators are actually known as the left and right shift operators. They perform bitwise shifting. For example, 5 << 1 will shift all the bits in 5 (0101) left one place, to get 10 (1010).

However, as with most other operators, you can overload the shift operators. In the case of the input/output library, the shift operators are overloaded to provide a natural syntax for input and output to a stream. That's because the directionality of the << and >> tokens look like something is flowing one way or the other. With these I/O classes, these operator overloads return a reference to the stream you're performing the operator on so that they can be chained together.

You can overload the shift operators for a particular class by either providing a member function operator<< or operator>> that takes one argument (the operand to the right of the operator). Alternatively, you can provide non-member function with the same names that take two arguments, the two operands of the operator respectively.

于 2012-10-17T16:39:51.230 回答
1

It's syntactic sugar for the following:

// Let the function 'print' be a renaming of 'operator<<'
// with T being the type of the object you want to print.
std::ostream& print(std::ostream&, const T&);

// 1) Print "first value is" and then return the stream you
// to which to just printed (ie. cout). 2) Use the returned
// stream to chain function calls and print 'firstValue'.
print(print(std::cout, "first value is"), firstValue);
于 2012-10-17T16:33:47.967 回答