4

I am learning c++ on my own. I was studying operator overloading, i was able to understand addition and subtraction operator overloading. But overloading of I/O operators is a bit confusing. I have created a class for Complex numbers, now i am overloading operators.

Function prototype from Complex.h

friend ostream& operator<<(ostream&, const Complex&);

Function from Complex.cpp

ostream& operator<<(ostream& os, const Complex& value){
os << "(" << value.r <<", "
<< value.i << ")" ;
return os;
}
  1. Can anyone explain (on a basic level) why we have to use a friend function declaration here?
  2. Why do we have to pass all arguments and the return type of the operator by reference?
  3. This function works fine without using const, but why are we using const here? What is the advantage of passing Complex as a constant reference?
user3834119
  • 411
  • 9
  • 21
  • 3
    You show 2 differents function, `operator<<` and `Complex::operator/`... Copy/Paste error? If not, specify the problem on each one. – Holt Jul 13 '14 at 13:48
  • +1 question looks clear to me. – fonZ Jul 13 '14 at 14:50
  • @Deduplicator I can't spot any answer there, directly referring to these 3 points in the OP's question. If so, it's pretty buried in there and just accessible via folloowing additional links. – πάντα ῥεῖ Jul 13 '14 at 15:11
  • 1
    @πάνταῥεῖ: Right, it does not explain the reasons for those points. Retracted. Still, that should be read by the op anyway: [Operator overloading](https://stackoverflow.com/questions/4421706/operator-overloading) – Deduplicator Jul 13 '14 at 15:13

6 Answers6

3

You do not have to make the streaming operator a friend. It does have to be externally declared as the Complex object is on the right-hand side of the operator.

However if your Complex class has a way to access the members required (possibly through getters) you could get the streaming operator to use them. Say:

std::ostream& operator<<( std::ostream& os, Complex const& cpx )
{
   return os << cpx.getReal() << ',' << cpx.getImaginary();
}

Your operator/ overload can be done as an internal function but actually is better implemented also as an external function with two const& parameters. If it is a member function it needs to be a const-member. Yours is not.

You might implement it based on operator /= thus

Complex operator/ ( Complex const& lhs, Complex const& rhs )
{
    Complex res( lhs );
    res /= rhs; // or just put return in front of this line
    return res; 
}
CashCow
  • 30,981
  • 5
  • 61
  • 92
2

For friend ostream& operator<<(ostream&,const Complex&); :

  1. Because you declare a free function here, and would like it to access the internals (private/protected) of your Complex objects. It is very common to have "friend free functions" for those overloads, but certainly not mandatory.

  2. Because streams are non copyable (it does not make sense, see also this post), passing by value would require a copy. Passing Complex by value would also require a useless copy.

  3. Because those output operators are not supposed to modify the objects they are working on (Input operators are, obviously), so add const to ensure that.

Community
  • 1
  • 1
quantdev
  • 23,517
  • 5
  • 55
  • 88
  • Can you explain that why streams are non copyable? – user3834119 Jul 13 '14 at 14:53
  • @user3834119, certainly, see this post : http://stackoverflow.com/questions/6010864/why-copying-stringstream-is-not-allowed/6010930#6010930 (I add it as an edit) – quantdev Jul 13 '14 at 14:55
1
  1. Can anyone explain (on a basic level) why we have to use a friend function declaration here?

    If you declare a class friend of another class, then you are saying that the friend class can access the private and protected attributes and functions of your class.
    In your case you declare ostream& operator<< as friend which means that within the body of that function the ostream class will be able to access private and protected functions and attributes of your complex class.

  2. Why do we have to pass all arguments and the return type of the operator by reference?

    Because it was written like that to avoid to make a copy of the ostream object. So your << overload will use the same object without making a copy each time you use it.

  3. This function works fine without using const, but why are we using const here? What is the advantage of passing Complex as a constant reference?

    const after the function means that you are not changing any attributes of the class, which you dont.

I think that is about right and i hope it helps, but if anyone wants to comment feel free.

Community
  • 1
  • 1
fonZ
  • 2,428
  • 4
  • 21
  • 40
  • 1: `ostream` was most definitely **not** declared a friend. 2: Which object? – Deduplicator Jul 13 '14 at 13:59
  • i corrected 2, and in 1 the operator is declared friend, same principle smaller scope, i think that is obvious. Or do i see that wrong @Deduplicator? – fonZ Jul 13 '14 at 14:03
  • within the operator body all private and protected attributes and functions of the Complex class can be accessed. – fonZ Jul 13 '14 at 14:05
0
friend ostream& operator<<(ostream&, const Complex&);

Here the function needs be friend because you may need to be able to access the private member of complex.

When you overload << for Complex class it is likely that you will need to access the data members of Complex class in the overloaded functions and the data members may be private. In case they are private, to access the data members in operator << you will need to make it a member of complex class which is not possible since the left side object of operator is of ostream class type so only option is to make it a friend of Complex and overlaod << using global scope function.

Now the object is returned by reference to avoid creation of multiple copy of the object as told by others. And the object has to be returned because it is going to support when the operator is using for cascaded write like `cout<

The argument is passed as a const because it in always a good practice to receive the object as const if it is not going to be changed in side the function. This makes it work on even const objects.

LearningC
  • 3,182
  • 1
  • 12
  • 19
  • 1
    How do you know that? DO you have access to the `Complex` class definition? – juanchopanza Jul 13 '14 at 13:53
  • No. but its a common example when you start reading C++. member data is usually private and operator<< needs to access the data member then if it is private, options are to define the function operator<< inside complex class which is not possible so need to make it a friend. ok i'll added it answer. – LearningC Jul 13 '14 at 13:56
  • The standard example also includes public accessors, so no need for an extra backdoor. – Deduplicator Jul 13 '14 at 13:58
  • @Deduplicator question means to know why it is so. so i mean to give the possible reason. – LearningC Jul 13 '14 at 14:12
  • @LearningC: We only objected to you stating that access to the private parts was definitely neccessary. After the change, there's nothing to object to left – Deduplicator Jul 13 '14 at 14:29
  • @Deduplicator ya ok. i got it. i added details to answer to indicate that it is not necessary for data members to be private and to be accessed. – LearningC Jul 13 '14 at 14:37
0

For non-friend member function operators, there is only one argument, the right-hand side of the operator. So for your example with the division operator you can do like this:

Complex a = ...;
Complex b = ...;

Complex c = a / b;

and the compiler will actually see the last as

Complex c = a.operator/(b);

This means that for e.g. the output operator, if it's a normal member function you "output" to an instance of the class. in other words it would be used like

Complex a = ...;
Complex b = ...;
a << b;  // Really a.operator<<(b);

Clearly this is not what's wanted when implementing an operator that should output to a stream. Therefore we need to use a non-member function, which can take two different arguments.


For the second point, remember that the default method of passing arguments is by value, which means that the values are copied. For smaller classes it doesn't matter, but if there's a lot of data, or the class has a complex copy-constructor, then that might incur quite a performance hit. Also, some types and classes can't be copied, for example std::istream so you have to pass them by reference.


For the last point, by making some arguments const you tell both the compiler and the user of the function that it will not change the argument. Besides making it easier for users of the function to know that calling the function will not have any side-effects, it will also allow the compiler to possibly do some optimizations.


Generally regarding operator overloading, you might want to read this reference on the subject.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
0

Can anyone explain why we have to use friend function here?

A friend of a class can access private or protected variables/functions from the class in which it is specified as a friend. It is necessary to make the I/O operators a friend of class Complex if they need to access any private functionality. If not, one may simply define the function outside of the class without declaring it as such.

Why do we have to pass operator by reference?

I'm not sure what you mean. The operator is a function we are defining, not a parameter that is being passed anywhere. Are you referring to the parameters that it takes? Well, std::ostream is taken as a reference type because taking it by value would cause a copy, something which stream classes do not allow. Try it and you will get a compiler error. I will touch on why Complex is taken as a reference type below.

This function works fine without using const, but why are we using const here? What is the advantage of passing Complex as a constant object?

First of all, why not use const? The const type modifier is a clear statement to the compiler that we will not change the object in any way. It also makes it clear to the maintainers of your code. Besides, writing out an object typically requires the value, meaning no modifications to the object are necessary.

Using a reference to const also allows you to pass rvalues or tempoaries to the function, which are not possible with a reference to non-const, which only take lvalues. For example:

struct S { };

void f(S&);
void h(S const&);

int main()
{
    S s;

    f(s); // OK
    f(S()); // ERROR!

    h(s); // OK
    h(S()); // OK
}
David G
  • 94,763
  • 41
  • 167
  • 253
  • Can you explain that why stream classes do not allow to make a copy? – user3834119 Jul 13 '14 at 15:01
  • @user3834119 Because there's no consensus on what the correct semantics are for copying a stream. Streams have pointers to stream buffers that represent their external devices. Should copying the stream also copy that pointer? Who would then be in charge of handling the destruction of the buffer? Stream *buffers* can be copied as of C++11 but not the actual streams themselves. Streams can be moved but most compilers don't support this new feature yet. – David G Jul 13 '14 at 15:12