Welcome to Dream.In.Code
Getting C++ Help is Easy!

Join 105,772 C++ Programmers for FREE! Ask your question and get quick answers from experts. There are 1,455 online right now! We've got more than 500 tutorials and 2,000 snippets. Join and find out why Dream.In.Code is the #1 programming help community on the internet! Registration is fast and FREE... Join Now!



Overloading Operators (revised v.2)

 
Reply to this topicStart new topic

> Overloading Operators (revised v.2), Updated, streamlined, added mroe examples

Rating  4
KYA
Group Icon



post 23 Sep, 2007 - 11:25 PM
Post #1


Operator Overloading

What is it? It is the coder’s ability to add additional abilities to operators such as +, - *, %, etc… Here is a complete list of operators that can be overloaded in C++:

CODE
* / + - % ^ & | ! , = < > <= >= ++ -- << >> == != && || *= /= %= ^= &= |= += -= <<= >>= -> ->* [] () new delete


These operators cannot be overloaded:

CODE
., .*, ::, ?:, and sizeof


Below is an example of using an increment operator without overloading it

CODE
#include <iostream>
using namespace std;

class Counter
{
public:
    Counter();
    ~Counter();
    int GetItsVal()const {return itsVal;}
    void SetItsVal(int x) {itsVal = x;}
    void Increment() {++itsVal;}

private:
    int itsVal;
};

Counter::Counter():
itsVal(0)
{}

Counter::~Counter()
{}

int main()
{
    Counter i; //declares a counter object'i'
    cout << "The value of i is " << i.GetItsVal() << endl;
    i.Increment();
    cout << "The value of i is " << i.GetItsVal() << endl;
    return 0;
}
}


Output: The value of i is 0
The value of 1 is 1

Now this works; it allows the class to be incremented but it does not truly overload the ++ operator yet. Plus this method can eventually get too cumbersome to use. So how does one overload the prefix increment operator?

Default syntax for overloading an operator:
CODE
returnType operator op()


example:

CODE
void operator++()


This sample code shows how to overload the ++ operator:

CODE
#include <iostream>
using namespace std;

class Counter
{
public:
    Counter();
    ~Counter();
    int GetItsVal()const {return itsVal;}
    void SetItsVal(int x) {itsVal = x;}
    void Increment() {++itsVal;}
    void operator++() {++itsVal;}

private:
    int itsVal;
};

Counter::Counter():
itsVal(0)
{}

Counter::~Counter()
{}

int main()
{
    Counter i; //declares a counter object'i'
    cout << "The value of i is " << i.GetItsVal() << endl;
    i.Increment();
    cout << "The value of i is " << i.GetItsVal() << endl;
    ++i;
    cout << "The value of i is " << i.GetItsVal() << endl;
    return 0;
}


Output: The value of i is 0
The value of i is 1
The value of i is 2

In the code, the operator ++ is overloaded. Albeit simple, it simply increments the Counter object's private variable itsVal. The use of this operator being overloaded brings it closer to the use of increment operators when used on an integer, etc...

Now say for example, if you wanted to assign a new Counter object 'a' to the value that 'i' currently has like this:

CODE
Counter a = ++i;


This will fail. The code intends to assign the value of 'i' to a new Counter object 'a'. The program's built in copy constructor can handle the assignment, but the current overloaded operator function is void, it is not set to return anything, much less a Counter object. One possible solution is to create a temporary object and return that:

CODE
#include <iostream>
using namespace std;

class Counter
{
public:
    Counter();
    ~Counter();
    int GetItsVal()const {return itsVal;}
    void SetItsVal(int x) {itsVal = x;}
    void Increment() {++itsVal;}
    Counter operator++();

private:
    int itsVal;
};

Counter::Counter():
itsVal(0)
{}

Counter::~Counter()
{}

Counter Counter::operator ++()
{
    ++itsVal;
    Counter temp; //temp counter object created here
    temp.SetItsVal(itsVal);
    return temp;
}

int main()
{
    Counter i; //declares a counter object'i'
    cout << "The value of i is " << i.GetItsVal() << endl;
    i.Increment();
    cout << "The value of i is " << i.GetItsVal() << endl;
    ++i;
    cout << "The value of i is " << i.GetItsVal() << endl;
    Counter a = ++i;
    cout << "The value of a: " << a.GetItsVal();
    cout << " and i: " << i.GetItsVal() << endl;
    return 0;
}


Output: The value of i is 0
The value of i is 1
The value of i is 2
The value of a: 3 and i: 3

The operator++ function now creates a temp object and can be used when transferring data from one class object to another. The temp value that is returned is immediately assigned to a. Since a temp object must be created and later destroyed, this way of accomplishing the problem will eventually cause problems as well, way too much overhead and potentially a very expensive operation.

To make this use less memory, we will use the ‘this’ pointer:

CODE
#include <iostream>
using namespace std;

class Counter
{
public:
    Counter();
    ~Counter();
    int GetItsVal()const {return itsVal;}
    void SetItsVal(int x) {itsVal = x;}
    void Increment() {++itsVal;}
    const Counter& operator++();

private:
    int itsVal;
};

Counter::Counter():
itsVal(0)
{}

Counter::~Counter()
{}

const Counter& Counter::operator ++()
{
    ++itsVal;
    return *this;
}

int main()
{
    Counter i; //declares a counter object'i'
    cout << "The value of i is " << i.GetItsVal() << endl;
    i.Increment();
    cout << "The value of i is " << i.GetItsVal() << endl;
    ++i;
    cout << "The value of i is " << i.GetItsVal() << endl;
    Counter a = ++i;
    cout << "The value of a: " << a.GetItsVal();
    cout << " and i: " << i.GetItsVal() << endl;
    return 0;
}


Output: The value of i is 0
The value of i is 1
The value of i is 2
The value of a: 3 and i: 3

Using ‘this’ has made the code cleaner and more efficient.

Now, we have gone over overloading the prefix operator, but what about the postfix one? Instead of incrementing and then fetching, we must now fetch and then increment. For the initial code for this we will be creating a temporary object again, why? Consider this following segment:

CODE
a = x++;


If x has a value of 6, 'a' is assigned that value and x then becomes 7. If x is an object in this instance, the postfix increment operator needs to stash x's original value away, increment, then assign the new value to a. However this cannot be passed by reference, because immediately after it's done the temp object will go out of scope:

CODE
#include <iostream>
using namespace std;

class Counter
{
public:
    Counter();
    ~Counter();
    int GetItsVal()const {return itsVal;}
    void SetItsVal(int x) {itsVal = x;}
    void Increment() {++itsVal;}
    const Counter& operator++(); //prefix
    const Counter operator++(int); //postfix

private:
    int itsVal;
};

Counter::Counter():
itsVal(0)
{}

Counter::~Counter()
{}

const Counter& Counter::operator ++()
{
    ++itsVal;
    return *this;
}

const Counter Counter::operator ++(int theFlag)
{
    Counter temp(*this);
    ++itsVal;
    return temp;
}

int main()
{
    Counter i; //declares a counter object'i'
    cout << "The value of i is " << i.GetItsVal() << endl;
    i.Increment();
    cout << "The value of i is " << i.GetItsVal() << endl;
    ++i;
    cout << "The value of i is " << i.GetItsVal() << endl;
    Counter a = ++i;
    cout << "The value of a: " << a.GetItsVal();
    cout << " and i: " << i.GetItsVal() << endl;
    a = i++;
    cout << "The value of a: " << a.GetItsVal();
    cout << " and i: " << i.GetItsVal() << endl;
    return 0;
}


Output: The value of i is 0
The value of i is 1
The value of i is 2
The value of a: 3 and i: 3
The value of a: 3 and i: 4

The parameter passed into the postfix operator is used simply to differentiate and that the compiler knows which one to use, the actual value of 'theFlag' is never used.

Now to overloading binary operators:

Binary Operators

Unlike increments/decrements, operators such as addition (+), multiplication (*), division (/), and subtraction (-) will have to deal with two numbers, two objects, two of anything etc... to perform their function. Having this in mind, overloading their function will be different then the examples above:

CODE
#include <iostream>
using namespace std;

class Counter
{
public:
    Counter();
    Counter(int initialValue);
    ~Counter();
    int GetItsVal()const {return itsVal;}
    void SetItsVal(int x) {itsVal = x;}
    void Increment() {++itsVal;}
    Counter Add(const Counter &);

private:
    int itsVal;
};

Counter::Counter():
itsVal(0)
{}

Counter::Counter(int initialValue):
itsVal(initialValue)
{}
Counter::~Counter()
{}

Counter Counter::Add(const Counter &rhs)
{
    return Counter(itsVal + rhs.GetItsVal());
}

int main()
{
    Counter varOne(2), varTwo(4), varThree;
    varThree = varOne.Add(varTwo);
    cout << "varOne: " << varOne.GetItsVal() << endl;
    cout << "varTwo: " << varTwo.GetItsVal() << endl;
    cout << "varThree: " << varThree.GetItsVal() << endl;
    return 0;
}


Output: varOne: 2
varTwo: 4
varThree: 6

The Add() function is new in this listing and takes a const reference to a Counter object; it returns a Counter object. An additional constructor is created because varOne and varTwo need to be initialized to a nonzero value anmd the default one would not suffice.

Although we have defined a new function Add(), it does not overload the operator+, here is it overloaded:

CODE
#include <iostream>
using namespace std;

class Counter
{
public:
    Counter();
    Counter(int initialValue);
    ~Counter();
    int GetItsVal()const {return itsVal;}
    void SetItsVal(int x) {itsVal = x;}
    void Increment() {++itsVal;}
    Counter operator+ (const Counter &);

private:
    int itsVal;
};

Counter::Counter():
itsVal(0)
{}

Counter::Counter(int initialValue):
itsVal(initialValue)
{}
Counter::~Counter()
{}

Counter Counter::operator+(const Counter &rhs)
{
    return Counter(itsVal + rhs.GetItsVal());
}

int main()
{
    Counter varOne(2), varTwo(4), varThree;
    varThree = varOne + varTwo;
    cout << "varOne: " << varOne.GetItsVal() << endl;
    cout << "varTwo: " << varTwo.GetItsVal() << endl;
    cout << "varThree: " << varThree.GetItsVal() << endl;
    return 0;
}


Output: varOne: 2
varTwo: 4
varThree: 6

Note that the output is exaclty the same, excpet we have just overloaded the operator+. You can use this method to overload any arithmetic type, add, subtract, etc...

Overloading operators << and []

This next portion assumes you understand how ‘friend’ functions work. The syntax for these operators is essentially the same as above, but requires modification in regards to what classes/objects they are being placed into:

CODE
type operator[](parameter list)
type operator<<(parameter list)


This code is a good example of how both of these are used in regards to strings. When you have printed a string in the past it was:

CODE
cout << theString.GetString();


What we “want” to do is this:

CODE
cout << theString;


See here:

CODE
#include <iostream>
#include <string.h>
using namespace std;

class String
{
public:
    String();
    String(const char *const);
    String(const String &);
    ~String();

    //overloaded operators
    char & operator[](int offset);
    char operator[](int offset) const;
    String operator+(const String&);
    void operator+=(const String&);
    String & operator= (const String&);
    friend ostream& operator<<
        (ostream& theStream, String& theString);
    int GetLen()const {return itsLen;}
    const char * GetString() const {return itsString;}

private:
    String (int);
    char * itsString;
    unsigned short itsLen;
};//end String class

String::String()
{
    itsString = new char[1];
    itsString[0] = '\0';
    itsLen = 0;
}

String::String(int len)
{
    itsString = new char [len+1];
    for(int i = 0; i <= len; i++)
        itsString[i] = '\0';
    itsLen = len;
}

String::String(const char * const cString)
{
    itsLen = strlen(cString);
    itsString = new char[itsLen+1];
    for (int i = 0; i <itsLen; i++)
        itsString[i] = cString[i];
    itsString[itsLen] = '\0';
}

String::String(const String & rhs)
{
    itsLen=rhs.GetLen();
    itsString = new char[itsLen+1];
    for (int i =0; i< itsLen; i++)
        itsString[i] = rhs[i];
    itsString[itsLen] = '\0';
}

String::~String()
{
    delete [] itsString;
    itsLen = 0;
}

String& String::operator=(const String & rhs)
{
    if (this == &rhs)
        return *this;
    delete [] itsString;
    itsLen = rhs.GetLen();
    itsString = new char [itsLen +1];
    for (int i = 0; i <itsLen; i++)
        itsString[i] = rhs[i];
    itsString[itsLen] = '\0';
    return *this;
}

char & String::operator [](int offset)
{
    if (offset > itsLen)
        return itsString[itsLen-1];
    else
        return itsString[offset];
}

char String::operator [](int offset) const
{
    if (offset > itsLen)
        return itsString[itsLen-1];
    else
        return itsString[offset];
}

String String::operator +(const String & rhs)
{
    int totalLen = itsLen + rhs.GetLen();
    String temp(totalLen);
    int i, j;
    for (i = 0; i < itsLen; i++)
        temp[i] = itsString[i];
    for(j = 0; j < rhs.GetLen(); j ++)
        temp[i] = rhs[j];
    temp[totalLen]='\0';
    return temp;
}

void String::operator +=(const String &rhs)
{
    unsigned short rhsLen = rhs.GetLen();
    unsigned short totalLen = itsLen + rhsLen;
    String temp(totalLen);
    int i, j;
    for (i=0; i<itsLen; i++)
        temp[i] = itsString[i];
    for(j=0, i=0; j<rhs.GetLen(); j++, i++)
        temp[i] = rhs[i-itsLen];
    temp[totalLen] = '\0';
    *this = temp;
}

ostream& operator<< (ostream& theStream, String& theString)
{
    theStream << theString.itsString;
    return theStream;
}

int main()
{
    String theString("Example of Overloading Operators\n");
    cout << theString;
    return 0;
}


By overloading the [] and << operators, we were able to display a string without an extra function call.


The Assignment Operator= ()

This operator comes in handy when you want to assign the value of one object to another. This can create confusion as there are shallow copies (just the members) and a deep copy (which allocates the necessary memory for that object). Example:

CODE
Cat catOne(5,7);
         Cat catTwo(3,4);
        // some other code....
        catTwo = catOne;


This snippet has each object with a few of its assigned member values. What if you wanted to assign them to something else?

CODE
catTwo = catTwo;


I doubt anyone would do this on purpose, but it is possible for it happen by accident when references and dereferenced pointers hide the fact that this assignment is to itself. If this was not handled catTwo would delete its own memory allocation and then when it was ready to copy from the right side, there would be nothing there... For protection against this any assignment operator you create must check for itself using the 'this' pointer discussed earlier:

CODE
# include <iostream>
using namespace std;

class Cat
{
public:
    Cat();
    //copy constructor and destructor omitted
    int GetAge()const {return *itsAge;}
    int GetWeight()const {return *itsWeight;}
    void SetAge(int age){*itsAge = age;}
    Cat & operator=(const Cat&);

private:
    int *itsAge;
    int *itsWeight;
};

Cat::Cat()
{
    itsAge = new int;
    itsWeight = new int;
    *itsAge = 5;
    *itsWeight = 9;
}

Cat & Cat::operator=(const Cat & rhs)
{
    if (this == &rhs)
        return *this;
    *itsAge = rhs.GetAge();
    *itsWeight = rhs.GetWeight();
    return *this;
}

int main()
{
    Cat Frisky;
    cout << "Frisky's age: " << Frisky.GetAge() << endl;
    cout << "Setting Frisky's age to 6...\n";
    Frisky.SetAge(6);
    Cat Whiskers;
    cout << "Whisker's age: " << Whiskers.GetAge() << endl;
    cout << "copying Frisky to Whiskers....\n";
    Whiskers = Frisky;
    cout << "Whiskers's age: " << Whiskers.GetAge() << endl;
    return 0;
}


Output: Frisky's age: 5
Setting Frisky's age to 6...
Whisker's age: 5
copying Frisky to Whiskers
Whisker's age: 6

Data conversion was handled and would not cause an error is an object is assigned to itself. Going back to the Counter example, what happens if you wished to assign an integer value to a Counter object? There would be an error. This is how to handle such problems:

CODE
#include <iostream>
using namespace std;

class Counter
{
public:
    Counter();
    Counter(int val);
    ~Counter();
    int GetItsVal()const {return itsVal;}
    void SetItsVal(int x) {itsVal = x;}
    operator unsigned int();
private:
    int itsVal;
};

Counter::Counter():
itsVal(0)
{}

Counter::Counter(int val):
itsVal(val)
{}
Counter::~Counter()
{}

Counter::operator unsigned int()
{
    return (int (itsVal));
}

int main()
{
    Counter ctr(5);
    int someInt = ctr;
    cout << "someInt: " << someInt << endl;
    return 0;
}


By creating a conversion operator, the program now can switch between ints and Counter objects, vice versa, you can even add other data types, etc...


Limitations on Operaton Overloading

Operators for built in types (such as int, double) cannot be overloaded. Also the precedence order cannot be changed, nor the arity of the operator, i.e. unary or binary. As fun as it sounds, you can't create your own "new" operators like '**' being a power operator.
You could also make the operator+ subtract and the operator * divide, which shows the powerful abilities this has, but also shows impracticality. Code that is easier to read, smooth, concise, and practical is better then confusing pointless code. smile.gif

Hope this helped you in regards to overloading operators in C++. Any extra input/criticism would be greatly appreciated. smile.gif

This post has been edited by KYA: 27 Sep, 2007 - 06:47 PM
Go to the top of the page
+Quote Post


Register to Make This Ad Go Away!

jjhaag
Group Icon



post 27 Sep, 2007 - 03:59 PM
Post #2
having just given it a once-over, looks very nice. thanks for the resource.

one (very) small point:

"So how does one overload the prefix operator?"
should probably be
So how does one overload the prefix increment operator?

thanks again.

-jjh
Go to the top of the page
+Quote Post

KYA
Group Icon



post 27 Sep, 2007 - 06:46 PM
Post #3
QUOTE(jjhaag @ 27 Sep, 2007 - 03:59 PM) *

having just given it a once-over, looks very nice. thanks for the resource.

one (very) small point:

"So how does one overload the prefix operator?"
should probably be
So how does one overload the prefix increment operator?

thanks again.

-jjh



Thanks for pointing that out smile.gif I'll fix it smile.gif
Go to the top of the page
+Quote Post


Reply to this topicStart new topic
1 User(s) are reading this topic (1 Guests and 0 Anonymous Users)
0 Members:

 

Lo-Fi Version Time is now: 8/21/08 03:45PM

Live C++ Help!

C++ Tutorials

Reference Sheets

C++ Snippets

Bye Bye Ads

Free DIC T-Shirt

T-Shirt Example

Related Sites

Monthly Drawing

Thumb Drive

Partners

Top Contributors

Top 10 Kudos This Month