views:

700

answers:

11

I realize this is a basic question but I have searched online, been to cplusplus.com, read through my book, and I can't seem to grasp the concept of overloaded operators. A specific example from cplusplus.com is:

// vectors: overloading operators example
#include <iostream>
using namespace std;

class CVector {
  public:
    int x,y;
    CVector () {};
    CVector (int,int);
    CVector operator + (CVector);
};

CVector::CVector (int a, int b) {
  x = a;
  y = b;
}

CVector CVector::operator+ (CVector param) {
  CVector temp;
  temp.x = x + param.x;
  temp.y = y + param.y;
  return (temp);
}

int main () {
  CVector a (3,1);
  CVector b (1,2);
  CVector c;
  c = a + b;
  cout << c.x << "," << c.y;
  return 0;
}

from http://www.cplusplus.com/doc/tutorial/classes2/ but reading through it I'm still not understanding them at all. I just need a basic example of the point of the overloaded operator (which I assume is the "CVector CVector::operator+ (CVector param)").

There's also this example from wikipedia:

 Time operator+(const Time& lhs, const Time& rhs)
 {
   Time temp = lhs;
   temp.seconds += rhs.seconds;
   if (temp.seconds >= 60)
   {
     temp.seconds -= 60;
     temp.minutes++;
   }
   temp.minutes += rhs.minutes;
   if (temp.minutes >= 60)
   {
     temp.minutes -= 60;
     temp.hours++;
   }
   temp.hours += rhs.hours;
   return temp;
 }

from "http://en.wikipedia.org/wiki/Operator_overloading"

The current assignment I'm working on I need to overload a ++ and a -- operator.

Thanks in advance for the information and sorry about the somewhat vague question, unfortunately I'm just not sure on it at all.

A: 

The "operator" in this case is the + symbol.

The idea here is that an operator does something. An overloaded operator does something different.

So, in this case, the '+' operator, normally used to add two numbers, is being "overloaded" to allow for adding vectors or time.

EDIT: Adding two integers is built-in to c++; the compiler automatically understands what you mean when you do

int x, y = 2, z = 2; 
x = y + z;

Objects, on the other hand, can be anything, so using a '+' between two objects doesn't inherently make any sense. If you have something like

Apple apple1, apple2, apple3;
apple3 = apple1 + apple2;

What does it mean when you add two Apple objects together? Nothing, until you overload the '+' operator and tell the compiler what it is that you mean when you add two Apple objects together.

Michael Todd
Unfortunately, that explanation doesn't make much sense to me...the + symbol seems fairly useless, wouldn't this program (using the Time program as an example) work fine without it? Why is it there? Sure, I get it does "something different"...but that doesn't explain what it's doing, why it's doing it, or the point of it?
Jeff
Yes, it would work fine without it. But operator overloading may give a more "natural" syntax. Would you rather write b*b-4*a*c or subtract(multiply(b,b),multiply(multiply(4,a),c))?
dan04
+4  A: 

An operator in C++ is just a function with a special name. So instead of saying Add(int,int) you say operator +(int,int).

Now as any other function, you can overload it to say work on other types. In your vector example, if you overload operator + to take CVector arguments (ie. operator +(CVector, CVector)), you can then say:

CVector a,b,res;
res=a+b;

Since ++ and -- are unary (they take only one argument), to overload them you'd do like:

type operator ++(type p)
{
  type res;
  res.value++;

  return res;
}

Where type is any type that has a field called value. You get the idea.

Blindy
Doesn't the operator need to either be a member of one of the operand types or at least a friend?
Ben Voigt
Nope, you can define them outside of the class if you want. I think the only exception might be the = operator.
Maynza
That is correct, you can define out-of-class, global operators.
Blindy
A: 

A operator would be "+", "-" or "+=". These perform different methods on existing objects. This in fact comes down to a method call. Other than normal method calls these look much more natural to a human user. Writing "1 + 2" just looks more normal and is shorter than "add(1,2)". If you overload an operator, you change the method it executes.

In your first example, the "+" operator's method is overloaded, so that you can use it for vector-addition.

I would suggest that you copy the first example into an editor and play a little around with it. Once you understand what the code does, my suggestion would be to implement vector subtraction and multiplication.

ajmurmann
+13  A: 

Operator overloading is the technique that C++ provides to let you define how the operators in the language can be applied to non-built in objects.

In you example for the Time class operator overload for the + operator:

Time operator+(const Time& lhs, const Time& rhs);

With that overload, you can now perform addition operations on Time objects in a 'natural' fashion:

Time t1 = some_time_initializer;
Time t2 = some_other_time_initializer;

Time t3 = t1 + t2;    // calls operator+( t1, t2)

The overload for an operator is just a function with the special name "operator" followed by the symbol for the operator being overloaded. Most operators can be overloaded - ones that cannot are:

.  .*  :: and ?:

You can call the function directly by name, but usually don't (the point of operator overloading is to be able to use the operators normally).

The overloaded function that gets called is determined by normal overload resolution on the arguments to the operator - that's how the compiler knows to call the operator+() that uses the Time argument types from the example above.

One additional thing to be aware of when overloading the ++ and -- increment and decrement operators is that there are two versions of each - the prefix and the postfix forms. The postfix version of these operators takes an extra int parameter (which is passed 0 and has no purpose other than to differentiate between the two types of operator). The C++ standard has the following examples:

class X {
public:
    X&   operator++();      //prefix ++a
    X    operator++(int);   //postfix a++
};

class Y { };

Y&   operator++(Y&);        //prefix ++b
Y    operator++(Y&, int);   //postfix b++

You should also be aware that the overloaded operators do not have to perform operations that are similar to the built in operators - being more or less normal functions they can do whatever you want. For example, the standard library's IO stream interface uses the shift operators for output and input to/from streams - which is really nothing like bit shifting. However, if you try to be too fancy with your operator overloads, you'll cause much confusion for people who try to follow your code (maybe even you when you look at your code later).

Use operator overloading with care.

Michael Burr
+1 for the operators you can't overload.
FrustratedWithFormsDesigner
Thank you for the assistance...sadly while I do understand overloaded operators better, my understanding on how to use them is still no where near high enough for me to want to use them. But at least I have a better understanding now, thank you again!
Jeff
+1 Fantastic answer. It never actually occurred to me that the usage of `<<` and `>>` for streaming was actually due to operator overloading in the standard library. I always thought it was just one of those quirks of C++ that these operators just meant different things depending on the context.
LeopardSkinPillBoxHat
@Jeff: You do not need to overload operators to program good c++ (unless maybe the `operator()` at one point or another). Consider them only when: a) it will be more readable to use `a X b` for any given operator `X` and instances `a` and `b` of your type; and b) it does make sense from a domain point of view (adding matrices is natural, adding `car + another_car` would be weird, using `car += petrol` can be understood, but is not natural either...)
David Rodríguez - dribeas
A: 

An overloaded operator is when you use an operator to work with types that C++ doesn't "natively" support for that operator.

For example, you can typically use the binary "+" operator to add numeric values (floats, ints, doubles, etc.). You can also add an integer type to a pointer - for instance:

char foo[] = "A few words";
char *p = &(foo[3]);     // Points to "e"
char *q = foo + 3;       // Also points to "e"

But that's it! You can't do any more natively with a binary "+" operator.

However, operator overloading lets you do things the designers of C++ didn't build into the language - like use the + operator to concatenate strings - for instance:

std::string a("A short"), b(" string.");
std::string c = a + b;  // c is "A short string."

Once you wrap your head around that, the Wikipedia examples will make more sense.

Bob Murphy
A: 

Before starting out, there are many operators out there! Here is a list of all C++ operators: list.

With this being said, operator overloading in C++ is a way to make a certain operator behave in a particular way for an object.

For example, if you use the increment/decrement operators (++ and --) on an object, the compiler will not understand what needs to be incremented/decremented in the object because it is not a primitive type (int, char, float...). You must define the appropriate behavior for the compiler to understand what you mean. Operator overloading basically tells the compiler what must be accomplished when the increment/decrement operators are used with the object.

Also, you must pay attention to the fact that there is postfix incrementing/decrementing and prefix incrementing/decrementing which becomes very important with the notion of iterators and you should note that the syntax for overloading these two type of operators is different from each other. Here is how you can overload these operators: Overloading the increment and decrement operators

Partial
A: 

Another use of operator overloading, AFAIK unique to C++, is the ability to overload the assignment operator. If you have:

class CVector
{
    // ...
    private:
        size_t  capacity;
        size_t  length;
        double* data;
};

void func()
{
    CVector a, b;
    // ...
    a = b;
}

Then a.data and b.data will point to the same location, and if you modify a, you affect b as well. That's probably not what you want. But you can write:

CVector& CVector::operator=(const CVector& rhs)
{
    delete[] data;
    capacity = length = rhs.length;
    data = new double[length];
    memcpy(data, rhs.data, length * sizeof(double));
    return (*this);
}

and get a deep copy.

dan04
`delete data` invokes undefined behavior, it should be `delete[] data`.
FredOverflow
+2  A: 

What you found in those references are not bad examples of when you'd want operator overloading (giving meaning to vector addition, for example), but they're horrible code when it comes down to the details.

For example, this is much more realistic, showing delegating to the compound assignment operator and proper marking of a const member function:

class Vector2
{
  double m_x, m_y;
public:
  Vector2(double x, double y) : m_x(x), m_y(y) {}
  // Vector2(const Vector2& other) = default;
  // Vector2& operator=(const Vector2& other) = default;

  Vector2& operator+=(const Vector2& addend) { m_x += addend.m_x; m_y += addend.m_y; return *this; }
  Vector2 operator+(const Vector2& addend) const { Vector2 sum(*this); return sum += addend; }
};
Ben Voigt
A: 

Operator overloading allows you to give own meaning to the operator. For example, consider the following code snippet:

char* str1 = "String1"; char* str2 = "String2"; char str3[20];

str3 = str1 + str2;

You can overload the "+" operator to concatenate two strings. Doesn't this look more programmer-friendly?

webgenius
how is that overloading? That just looks like adding (ie int str1 = 2; int str2 = 3, int str 3 = str1 + str2;), how is one overloading and the other not?
Jeff
David Rodríguez - dribeas
+2  A: 

From your comments above, you dont see the point of all this operator overloading?

Operator overloading is simply 'syntactic sugar' hiding a method call, and making code somehwhat clearer in many cases.

Consider a simple Integer class wrapping an int. You would write add and other arithmetic methods, possibly increment and decrement as well, requiring a method call such as my_int.add(5). now renaming the add method to operator+ allows my_int + 5, which is more intuitive and clearer, cleaner code. But all it is really doing is hiding a call to your operator+ (renamed add?) method.

Things do get a bit more complex though, as operator + for numbers is well understood by everyone above 2nd grade. But as in the string example above, operators should usually only be applied where they have an intuitive meaning. The Apples example is a good example of where NOT to overload operators. But applied to say, a List class, something like myList + anObject, should be intuitively understood as 'add anObject to myList', hence the use of the + operator. And operator '-' as meaning 'Removal from the list'.

As I said above, the point of all this is to make code (hopefully) clearer, as in the List example, which would you rather code? (and which do you find easier to read?) myList.add( anObject ) or myList + onObject? But in the background, a method (your implementation of operator+, or add) is being called either way. You can almost think of the compiler rewritting the code: my_int + 5 would become my_int.operator+(5)

All the examples given, such as Time and Vector classes, all have intuitive definitions for the operators. Vector addition... again, easier to code (and read) v1 = v2 + v3 than v1 = v2.add(v3). This is where all the caution you are likely to read regarding not going overboard with operators in your classes, because for most they just wont make sense. But of course there is nothing stopping you putting an operator & into a class like Apple, just dont expect others to know what it does without seeing the code for it!

'Overloading' the operator simply means your are supplying the compiler with another definition for that operator, applied to instances of your class. Rather like overloading methods, same name... different parameters...

Hope this helps...

Beware of what definitions can be considered 'intuitive'. What does 'a+b' mean for two vectors? Is it adding each paired element together? Will it create a new vector out of appending the contents of `b` into a copy of `a`? Only overload an operator when it has an unique clear meaning. Adding two complex numbers is well understood and there can be no misinterpretation.
David Rodríguez - dribeas
Vector addition is as well defined as for complex numbers... addition should return a new vector such that (in 2D) the new vector = (x1+x2, y1+y2).... where the original vectors are (x1, y1) and (x2, y2)... What else could it mean??? I only mentioned Vectors because an example in the OP used them...
Upon reflection I see your thinking of a Vector in the data structure sense, where the examples (and how I used it) was in the mathematical sense. Still, the reason the data structure is called a Vector is related to its mathematical roots, so the operations like V1 + V2 should be defined the same, meaning it is only valid if the elements have an operator + defined upon them. Then V1 + V2 = a new Vecor V3 such that V3_i = V1_i + V2_i, with 0 <= i < |Vector|. (all vector should be of same length). This really just amounts to a N-dimensional Vector, where physics etc uses 3D.
Perhaps this is why newer data structure libraries use List or ArrayList instead of Vector? To avoid such confusion of terms?
A: 

The accepted answer by Michael Burr is quite good in explaining the technique, but from the comments it seems that besides the 'how' you are interested in the 'why'. The main reasons to provide operator overloads for a given type are improving readability and providing a required interface.

If you have a type for which there is a single commonly understood meaning for an operator in the domain of your problem, then providing that as an operator overload makes code more readable:

std::complex<double> a(1,2), b(3,4), c( 5, 6 );
std::complex<double> d = a + b + c;         // compare to d = a.add(b).add(c);
std::complex<double> e = (a + d) + (b + c); // e = a.add(d).add( b.add(c) );

If your type has a given property that will naturally be expressed with an operator, you can overload that particular operator for your type. Consider for example, that you want to compare your objects for equality. Providing operator== (and operator!=) can give you a simple readable way of doing so. This has the advantage of fulfilling a common interface that can be used with algorithms that depend on equality:

struct type {
   type( int x ) : value(x) {}
   int value;
};
bool operator==( type const & lhs, type const & rhs ) 
   { return lhs.value == rhs.value; }
bool operator!=( type const & lhs, type const & rhs ) 
   { return !lhs == rhs; }

std::vector<type> getObjects(); // creates and fills a vector
int main() {
   std::vector<type> objects = getObjects();
   type t( 5 );
   std::find( objects.begin(), objects.end(), t );
}

Note that when the find algorithm is implemented, it depends on == being defined. The implementation of find will work with primitive types as well as with any user defined type that has an equality operator defined. There is a common single interface that makes sense. Compare that with the Java version, where comparison of object types must be performed through the .equals member function, while comparing primitive types can be done with ==. By allowing you to overload the operators you can work with user defined types in the same way that you can with primitive types.

The same goes for ordering. If there is a well defined (partial) order in the domain of your class, then providing operator< is a simple way of implementing that order. Code will be readable, and your type will be usable in all situations where a partial order is required, as inside associative containers:

bool operator<( type const & lhs, type const & rhs )
{
   return lhs < rhs;
}
std::map<type, int> m; // m will use the natural `operator<` order

A common pitfall when operator overloading was introduced into the language is that of the 'golden hammer' Once you have a golden hammer everything looks like a nail, and operator overloading has been abused.

It is important to note that the reason for overloading in the first place is improving readability. Readability is only improved if when a programmer looks at the code, the intentions of each operation are clear at first glance, without having to read the definitions. When you see that two complex numbers are being added like a + b you know what the code is doing. If the definition of the operator is not natural (you decide to implement it as adding only the real part of it) then code will become harder to read than if you had provided a (member) function. If the meaning of the operation is not well defined for your type the same happens:

MyVector a, b;
MyVector c = a + b;

What is c? Is it a vector where each element i is the sum of of the respective elements from a and b, or is it a vector created by concatenating the elements of a before the elements of b. To understand the code, you would need to go to the definition of the operation, and that means that overloading the operator is less readable than providing a function:

MyVector c = append( a, b );

The set of operators that can be overloaded is not restricted to the arithmetic and relational operators. You can overload operator[] to index into a type, or operator() to create a callable object that can be used as a function (these are called functors) or that will simplify usage of the class:

class vector {
public:
   int operator[]( int );
};
vector v;
std::cout << v[0] << std::endl;

class matrix {
public:
   int operator()( int row, int column ); 
      // operator[] cannot be overloaded with more than 1 argument
};
matrix m;
std::cout << m( 3,4 ) << std::endl;

There are other uses of operator overloading. In particular operator, can be overloaded in really fancy ways for metaprogramming purposes, but that is probably much more complex than what you really care for now.

David Rodríguez - dribeas