0

I'm new to C++ and my questions are: Why do we need to overload operators in C++ and when should we overload operators in C++, why don't we just use built-in variables to do calculations, comparison, output and input value. I just don't get it, what is the most appropriate situation where we would need to overload operators or should we always overload operators when dealing with classes?

McGuy
  • 81
  • 1
  • 11
  • 3
    What if you want to use `+` on complex numbers that you implemented? – Maroun Aug 19 '14 at 11:45
  • As an example, what if you make a class named `Color`. If I ask you, what `Color` is produced by `Color('Red') + Color('Yellow')`, you would say `Color('Orange')`, correct? Obviously a color isn't a C++ primitive, but you could define what the `+` operator means. There are infinite other situations when this type of problem pops up. – Cory Kramer Aug 19 '14 at 11:46
  • In brief, whenever the resultant syntax would make sense for the type being used (e.g. a mathematical type or smart pointer). Be careful of the "tricks" and cute code, inevitably that makes it harder to maintain in the long run. – Niall Aug 19 '14 at 11:51
  • @Cyber. Why don't you just define two Strings instead of a class? Sorry if I don't follow but I'm relatively new to programming. – McGuy Aug 19 '14 at 12:06
  • 1
    Because `std::string` already has a `+` operator. If you had to add two strings, then `'Red' + 'Yellow' = 'RedYellow'` not `'Orange'`. Plus `Color` could do many other things that don't make sense with strings, like `lighten`, `saturate`, `greyscale`, etc. These would all be methods that only make sense for a given class. – Cory Kramer Aug 19 '14 at 12:09
  • Not a dupe of the indicated question. This question asks "why," whereas that question answers "how." – John Dibling Aug 19 '14 at 12:34
  • Usually you should not overload operators as day-to-day code gets unreadable very fast. On the other hand every class will provide a default implementation of operator= for assignment, which you should be aware of, and it's good practice to either make this operator explicit (to encourage its use or if you need to handle dynamic memory) or to make it private if assignment is not something your objects support. As with other operators: they are simply functions, functions with special syntax (infix, postfix, prefix) and ordering (^ before * before + before =, etc...) use them where appropriate! – BeyelerStudios Aug 19 '14 at 12:41
  • Now I get it. Thank you all for you help, I now completely understand. Thanks to all of those who answered. – McGuy Aug 19 '14 at 13:32
  • The most important reason i can think of is that you need operator== implemented in your Class in order to run std::find on a container of Class objects. I rarely implement other operators to be honest, but that's probably just my bad habit. – Martin G Aug 19 '14 at 13:50
  • @Martin If the reason is just to use `std::find`, rather than `std::find_if`, that's practically obfuscation. If the notion of equality is well defined for the object, however, you should implement both `==` and `!=`. (You should never implement one without implementing the other.) – James Kanze Aug 19 '14 at 14:00
  • Sometime, have a look at Boost spirit (www.boost.org). That's a real touchstone for appropriate operator overloading. – P45 Imminent Aug 19 '14 at 14:03

1 Answers1

2

If you look at a lot of C++ sources, you'd probably conclude that the most frequent use of operator overloading (or overloading in general, for that matter) is obfuscation. Still...

First and foremost: in a number of cases, you'll have to overload the assignment operator. The compiler will generate one for you if you don't, and in a number of cases, the generated one won't do what you want. (It's also frequent to declare a private overloaded assignment, and not implement it, in order to block assignment.)

If a class represents some sort of numerical value (e.g. BigInteger or Decimal), then it definitely makes sense to overload the arithmetic operators. It's a lot more readable to write:

BigFloat
discriminant( BigFloat const& a, BigFloat const& b, BigFloat const& c )
{
    return b*b - 4*a*c
}

than

BigFloat
discriminant( BigFloat const& a, BigFloat const& b, BigFloat const& c )
{
    return sub(mult(b, b), mult(4, mult( a, c )));
}

In such cases, you should always overload all applicable operators, with their natural meanings: it would be very uncool if your type supported +, but not +=, or if it supported + with the semantics of subtraction.

There are few legitimate extensions with regards to the arithmetic operators: about the only one I'd find acceptable is a string class using + (and +=) for concatenation. (Normally, I'd expect + to be commutative, which concatenation certainly isn't. But the convention is so well established in languages that have built-in string types that you can hardly avoid it.)

Types that are comparable should support == and !=, and if there is a logical ordering, <, <=, > and >=. (On the other hand, overloading < when there is only an arbitrary ordering, just so you can call std::sort without an comparison operator, is obfuscation.) Again, it's much more natural to write a < b than isLessThan( a, b ). It's sometimes difficult to decide: when I first started C++, I had a Set class, based on bitmaps, and I defined < to be a strict subset, <= a subset, etc. I don't know if I'd do this again; the inequality operators should probably only be defined if the relationship is transitive.

Beyond that, any time your type emulates in some way something built in, like a pointer or an array, you will probably want to overload the corresponding supported operators: a vector or an array class which didn't support [] would be surprizing, as would be a smart pointer which didn't support * and ->.

And finally, there are some standard C++ idioms which use operator overloading:

  • If you define a type which can be inserted or extracted from a text stream, you do so by defining the << and >> operators.

  • C++ iterators are designed to look like pointers; if you want iterators which can be used with the standard algorithms, they should support the same operators as those of a smart pointer, along with ++, and possibly --, and in some cases, all of the pointer arithmetic operators (including [], which is defined in terms of pointer arithmetic on raw pointers). This is clearly obfuscation, and it certainly makes using the iterators unnecessarily complicated, but it's hard to avoid if you're using the C++ standard library.

  • The C++ library also makes extensive use of functional objects, with an operator() (a function call operator). Predicate objects have an operator() which returns a bool. (One of the most frequent uses of predicate objects is to implement an ordering relationship. If your class doesn't support a logical < operator, you might want to provide a separate functional object which defines an arbitrary order, so that objects can still be inserted into std::set, or be used as a key in std::map.) Arguably, this too should have been a named function, but it's hard to imagine a good name that would apply everywhere, and since the object does behave like a function, it's not really too bad.

There are probably one or two other cases I've forgotten, but in general, with regards to operator overloading, when in doubt, don't.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • I'd have concluded the the most frequent use of operator overloading would have been assignation, even if it's the default one. – Jaffa Aug 19 '14 at 14:05
  • +1 but it might be worth mentioning `<<` and `>>` from STL streams (assuming you find them acceptable too). – P45 Imminent Aug 19 '14 at 14:07
  • @Geoffroy Yes. That one is so usual, it didn't occur to me to mention it, but it's true. – James Kanze Aug 19 '14 at 14:10
  • @YogiBear Agreed. In C++, `<<` and `>>` are the insertion and extraction operators (which are abusively overloaded for shifting on the integral types:-)). – James Kanze Aug 19 '14 at 14:11