I've pored over information regarding friend functions and their use. They're able to access encapsulated data within a class while not breaking one of the golden rules of OOP. In purveying various source code for overloading the I/O operators (A basic operation, one of the first taught in learning C++) every one defines the operator as a friend as implements it outside of the class. My question is: does this need to be done? Why not just declare the function as a public member of the class and insert/display data from the class while keeping everything encapsulated? It seems no different than overloading other operators, yet it is a supposedly traditional approach to overloading the I/O operators.
Thanks for your time.
Let's say you want to overload operator<< for your class X, so you can use it like this:
X x;
std::cout << x;
Notice that std::cout is the first operand of the operator. To implement this as a member function, it would have to be a member of std::basic_ostream, which is the type of std::cout. You can't add members to an already defined class. That's why we declare it as a free function instead.
If you overloaded operator<< as a member of X, it would be taking an X object as its first operand, so you would use it like this:
X x;
x << something;
This is obviously not what you want when dealing with I/O.
If you have an overloaded operator like: a # b implemented as a member function, that call is translated to a.operator#(b);. That means the function must be a member of the class that's the type of the left operand. In the case of iostreams, that would be all the operators needed to be members of the iostream itself.
While iostreams do provide some insertion/extraction operators as members, you normally want to be able to add more without modifying the iostream class itself1. To do that, you pretty much need to implement the operator as a free function instead of a member function. Since you normally still want it to have access to the private parts of whatever type you're planning to read/write (insert/extract, if you prefer) it typically has to be a friend of that class.
This is an example of the so-called open/closed principle: the class should be open to extension, but closed to modification. In other words, you want to extend it without modifying it.
Related
Why are functions of objects generally defined as class member functions instead of friend functions (with the associated instantiated object passed in as a parameter)?
If you implement push_back(vector v, val), front(vector v), back(vector) as friend functions that take as a parameter a vector, for example, would that not save on space, because the functions would not have to be defined each time a vector object is created, but only once?
I'm sorry if my question doesn't make much sense. I'm new to coding and I am not completely comfortable with its jargon/terminology. If I need to reword my question because it is unclear, please let me know.
Thank you :)
Unless they’re virtual, member functions don’t take up any space inside a class. And virtual member functions only take up constant space. This is usually 8 bytes, independent of how many virtual functions the class has. This is because classes with virtual functions contain a pointer to a vtable which looks up the actual function at runtime.
That being said, having friend functions at namespace scope is useful because it allows you to make overload sets. An overload set is just the name for all the overloads of a particular function name. For example, std::to_string is an overload set because there are multiple functions with that name.
Overload sets are a really useful concept because they allow you to write generic code that can act on a lot of different types, even if those types are completely unrelated For example, std::vector and std::list don’t inherit from each other or from a base class, but it’s easy to write templated functions that work on either of them because they share a common interface in terms of how they can be used.
If you implement push_back(vector v, val), front(vector v), back(vector) as friend functions that take as a parameter a vector, for example, would that not save on space, because the functions would not have to be defined each time a vector object is created, but only once?
Functions are not "defined each time a vector object is created". You don't save space by using friend functions as such.
The advantages of member functions mostly revolve around inheritance. Member functions can have protected access, and they can be virtual etc.
Friend functions are useful in implementing symmetric binary (i.e. two-argument, counting this) functions. As a member function the first argument would be special and cannot be treated equally. Furthermore, friend functions can provide an API compatible with C, which is useful for cross-language programming and shared libraries.
I would like to know if there is anything similar to a "friend function", from C++, in Fortran. I would like to be able to make operator overloading without creating new objects since it is too expensive. I have already tried creating objects in a module and trying to make the overloaded operator return it, but it was not successful.
Your understanding of what friend means in C++ is incorrect. That deals with the accessibility of private class members inside the friend function - not with object creation as part of operator overloading. Fortran's accessibility model is different - any procedure defined in the same module as a type is a "friend" and there is nothing equivalent, in accessibility terms, to a member function.
The semantics of expression evaluation in both languages requires that the source implementation of an operator create a new object to store the result of the operation. Fancy compiler optimisation may prevent the actual creation of a temporary for a function result in some cases, but that very much depends on details.
I'm a student. Let's say I want to be able to do my_string.reverse();. I don't want to see the actual code to reverse a string, I just want to see how this function would be implemented for strings.
A side question: why have I never seen this dot operator used with types such as int or char, is that simply not possible?
You can't overload the member access operator; and you can't add members to an already defined class. If the type of my_string doesn't have a reverse member, and you can't (or don't want to) add one to the class definition, then you simply can't do anything to provide that syntax.
If you want to write a function to manipulate a class without changing the class definition, it will have to be a non-member function, called as e.g. reverse(my_string).
why have I never seen this dot operator used with types such as int or char, is that simply not possible?
No it isn't. The language only defines that operator for class types, and doesn't allow it to be overloaded.
This is a bit of a general question, but I have some classes that I'd like to define some binary "operations" on, and there are several different ways of doing so. For example, suppose I have a Vector class that I'd like to implement an addition operation on. On one hand, I could just overload the '+' operator, but I have read from a few sources that this isn't a very good practice (which begs one to ask why this is a language feature at all). I can see in many cases why methods are preferable to operator overloading, but vector addition is widely agreed upon and so using '+' should be very natural.
On the other hand, I could define an add() method in the class. I could just define it as a normal method and use x.add(y) to perform x + y, but it doesn't showcase itself as a binary operator so I'm not sure if this should be preferred. I could also define it as a static method, for instance Vector.add(x, y). Finally, I could also define add() as a friend function of the class, which is very (mathematically) natural but in my opinion is a bit contrary to the philosophy of OOP. I'm hoping to get a bit of insight into which methods are preferable (and why).
The usual approach is to define reflexive operators (+=, *=, etc.) as members that modify the object they are applied to, and to define non-reflexive operators (+, *, etc.) as non-members that create a copy of one of their arguments, use the corresponding reflexive operator to do the operation, and return the new object as their result.
While Java programmers believe that add functions are a good thing, they do so because Java doesn't have operator overloading. Named operations lead to really long and unreadable expressions for things that should be simple.
The advantage to defining your binary operator as a friend function (potentially as a set of overloads) is to permit the left-hand value to be other than an instance of your class. This is commonly done for stream inserters/extractors. Note that the friend status is only necessary if your operator requires access to the internals of one of its operands. It's best if the operator can work through the public interfaces of each operand, avoiding the need for friend and the resulting tighter coupling.
In "The C++ programming language", at page 265, the author makes the following statement:
Because of historical accident, the operators = (assignment), & (address-of), and , (sequencing;
§6.2.2) have predefined meanings when applied to class objects. These predefined meanings can
be made inaccessible to general users by making them private:
Then the following example is given:
class X {
private:
void operator=(const X&);
void operator&();
void operator,(const X&);
// ...
};
void f(X a, X b)
{
a = b; // error: operator= private
&a; // error: operator& private
a,b; // error: operator, private
}
I can't quite understand what do these "error" comments refer to? Does that mean I should not define a function like f, or that all of the =, &, and , operators should be used according to the default way, and it is not necessary to redefine them?
This example simply shows a way to prevent yourself or other developers of the code from using operators, which can be used without having been defined in the class, because they're automatically generated (and have default meanings for the operations they represent).
The author of the example meant, that if you try to assign b to a (in line a = b) it will cause an error, because the assignment operator is private in the class definition.
Similar error occurs in case of address-of in the second line, and the comma operator in the third.
Making default operators/constructors private if you know they're not supposed to be used (or haven't been implemented yet) is good, because one may accidentally use a very frequent operator like assignment or copy-constructor, being unaware that it's default behavior conflicts with the class lifecycle. If such operator or constructor is made private at the very beginning of class design, the compiler will generate a compile-time error instead of performing a potentially dangerous operation without notice if the programmer accidentally uses the method.
Think default assignment operator and member pointer: it will copy the pointer whereas you might want the object to be the owner of data. Then, after someone assigns one object to another without knowing that assignment is not implemented, you will end up with a double free error. Instead of that, if the operator is private, you'll get a nice error and the code will not even compile, and you'll know what's going on.
The author intends to point out here that the operators =, & and , are usually implicitly available for a class.
So if you don't want your objects to be operated on through them then you declare them as private thus disallowing their use.
Since they are declared as private you cannot access them anymore outside the class and the compiler gives you a compilation error. The function is an example showing that.
Providing your own implementation of any operator is basically the same as implementing a class method. Operators and methods are the same in terms of accessibility. What you do is disallowing access to operators from the caller's code.
It's absolutely the same as if you defined a private method and then tried to call it from some code that is not part of your class. Just make the operators public and errors will go away.
It basically prevents any one from making an 'X' object and using the "=", "&", and "," operators on that class. Because the author of the class may implement those objects with a meaning that is quite different to what the consumer of the class might think they do ... so its best to prevent them being used at all in the case of ambiguity.
The function f is an example of a user trying to use the private operators. It shows you what code it is that you're preventing by making them private. The comment // error means that a program that contained that line would fail to compile for the stated reason.
Before discussing the error, a key here is to understand that these operations will be implicitly made available for your class. This is the essence of Scott Meyers' advice "Know what functions C++ silently writes and calls."
C++ will automatically implement the assignment operator for your class, but it may not be done correctly (for example, if your class contains a pointer member variable). By defining the assignment operator explicitly, you are telling the compiler to use your implementation instead of generating one for you. And by making it private, you are essentially disallowing assignment of one class instance to another. Anywhere you try to do this in your code, the compiler will complain, which is a good thing if you really don't want assignment to be done.
In function f the author is showing you that these statements will not compile because of how the operators are defined in the class. It is perfectly acceptable to redefine operators for your class, and sometimes it is definitely required (for example, to implement a deep copy of a pointer member variable in your class). The point of the example is to show that a) you can provide your own implementation of these operators for your class, and b) because of this you have control over whether the operators are supported and implemented correctly for your class.