|
| Thursday, October 5, 2006 |
| Overloaded Operators |
The following topics are for conceptual understanding only. Don't try to learn all the details of overloaded operators! It isn't worth it. Just understand the concepts below and then use my files StandardFunctions.h and StandardFunctions.cpp to actually create your new classes. If you deviate just a little from the code in these files, overloaded functions don't work well and give all kinds of strange errors. Keep very, very close to the code and the instructions in the comments of these two files. Delete any functions you don't want for your class. You can, of course, add additional functions that are not related to Constructors or Overloaded Operators.
Operator Overloading
- What you think of as "operators", in other words +, -, =, >, [ ], etc., are actually functions, called operator functions.
- When you have an operator such as + overloaded in C++, and you have two objects of a classSomeClassName x, y;
and x and y are initialized to some appropriate values, then you can writex + y
in your C++ code.
- The compiler changes commutative binary operators that don't change the values, such as x + y , into a global function: operator+(x,y).
- The compiler changes operators that change the value of an object, such as unary operators and assignment, into member functions. For instance, x += y becomes x.operator+=(y).
- This is only a syntactical convenience for you as a programmer. Overloaded operators are not necessary for a language, and some language theorists think they just add unnecessary complexity. For instance, Java decided not to include overloaded operators, but C# decided to include them.
- You can overload operators with unlike datatypes. For instance, you could overload SomeClass& operator+(SomeClass& left, int right) to add an integer to an object of SomeClass.
- You are also not restricted to any particular return data type. For instance, in the example just above, you could choose to return an int instead of SomeClass& .
The operator functions you can overload
- Functions that can be used to extend the built-in set of C++ operatorsmathematical operators (+, -, *, /, %, ++, --, etc...)
operator+, operator-, operator*, etc...relational operators (<, >, ==, etc...)
operator<, operator>, operator==, etc...stream operators(<<, >>)
operator<<, operator>>the assignment operators (=, +=, -=, *=, etc...)
operator=, operator+=, operator-=, etc...logical operators (&&, ||, !, etc...) (usually valid only for boolean types on each side, so these rarely have appropriate meaning for objects on both sides)
operator&&, operator||, operator!, etc...dereference operators ([], *, ->), but be careful because they can get rather technical.
operator[], operator*, operator->
- With the intrinsic data types (float, char, ...), these operators have unchangeable meaning. You can only overload operators on the classes you create.
- Each operator can be given a new meaning for your classes simply by writing a function that implements that meaning, but it is strongly suggested that you keep the meaning of the function close to what programmers would normally expect for that function. Don't make the + operator act like a minus.
- If the operator is normally commutative in mathematics, such as + is, then make your definition so that it does not matter which object is on which side.
- If the operator is not commutative, make your definition non-commutative also, in a manner that is logical for that operator.
- Note that the equals operator, =, is commutative in mathematics, but the assignment operator, =, in programming is not. The equality operator in C, ==, is the equivalent of the equals operator in mathematics, and it should be commutative.Member and non-member functions
- Your overloaded operators can be defined as member or non-member functions of your class.
- Member functions use the object on the left of the operator to call the function, and pass the object on the right as a parameter. Thus x += y becomes x.operator+=(y), the overloaded operator += function for that class. Normally member functions are used for operators that change the value of the object, such as assignment or unary operators.
- Non-member functions are global functions, and are usually defined as friends of the class. They send both objects as parameters to the global function. Thus x + y becomes operator+(x, y), the global operator+ function with two parameters. Usually global functions are used for operators that do not change the value of either parameter object. Also, since they are global functions, they can be defined as commutative binary functions, where the order of the two parameters does not matter.Standard functions and code for overloaded operator functions.
- Since overloaded functions can be very tricky, I have supplied "cookie cutter" code to create a class with almost all the possible overloaded functions.
- Follow these hyperlinks for the standard code:
StandardFunctions.h and StandardFunctions.C - Do not stray far from this code, or your overloaded operators may not work as expected.
- Except for substituting your class name for MyClass, keep all non-comment code exactly as is. Add your code only where it says:
// appropriate code goes here to ...
The Big Three
The "Big Three" probably should be the "Big Four"
- If you write a copy constructor, then you should also write operator= and
the destructor. This will help eliminate problems with implicit calls to
the copy constructor. Also, unless you specifically do not want a default
constructor, you should also define one of them. Remember, the default
constructor is called implicitly in an array-of-objects declaration.
#include <iostream>
using namespace std;
class SomeClass
{
public:
// Default constructor
SomeClass();
// Copy Constructor
SomeClass(SomeClass&);
// Destructor
~SomeClass();
// assignment operator
SomeClass& operator=(SomeClass&);
void setData(int d);
int getData();
private:
int data;
};
// Default Constructor of SomeClass
SomeClass::SomeClass()
{
cout << "Default Constructor of SomeClass" << endl;
data = 0;
}
// Copy Constructor of SomeClass
SomeClass::SomeClass(SomeClass& original)
{
cout << "Copy Constructor of SomeClass" << endl;
data = original.data;
}
// Destructor of SomeClass
SomeClass::~SomeClass()
{
cout << "Destructor of SomeClass" << endl;
}
// Assignment operator of SomeClass
SomeClass& SomeClass::operator=(SomeClass& rValue)
{
cout << "operator= of SomeClass" << endl;
data = rValue.data;
}
The Copy Constructor
- Copy constructors have the form:
SomeClass(SomeClass&
original);
-
When an object is passed-by-value, and a Copy Constructor
has been written, that Copy Constructor will be called instead of making a bit-wise copy.
#include <iostream>
using namespace std;
class SomeClass
{
public:
SomeClass();
// Copy Constructor
SomeClass(SomeClass&);
~SomeClass();
void setData(int d);
int getData();
private:
int data;
};
SomeClass::SomeClass()
{
cout << "Constructor of SomeClass" << endl;
data = 0;
}
// Copy Constructor of SomeClass
SomeClass::SomeClass(SomeClass& original)
{
cout << "Copy Constructor of SomeClass" << endl;
data = original.data;
}
SomeClass::~SomeClass()
{
cout << "Destructor of SomeClass" << endl;
}
void SomeClass::setData(int d)
{
data = d;
}
int SomeClass::getData()
{
return data;
}
// Now the copy constructor will be called.
void changeTheObject(SomeClass object)
{
object.setData(5);
}
int main()
{
SomeClass object;
cout << "Before call to changeTheObject: " << object.getData() <<
endl;
changeTheObject(object);
cout << "After call to changeTheObject: " << object.getData() <<
endl;
return 0;
}
Output
Constructor of SomeClass
Before call to changeTheObject: 0
Copy Constructor of SomeClass
Destructor of SomeClass
After call to changeTheObject: 0
Destructor of SomeClass
- The destructor will be called on the copy when the function
returns, but if you created a well designed copy, the destructor will work
as it should on that local object only.
The Destructor
- The destructor always has the same name as the class, but with a tilde character, ~ , in front.
- The destructor cannot have any parameters or return type. This means that there is one and only one destructor for each class.
- Usually the destructor is just an empty set of curly braces. The destructor is important if the class has requested resources, such as dynamic memory or open file streams, from the operating system. The destructor is normally used to release those resources.
-
If there are a series of destructors called because they go out of scope
together, they are called in the reverse order that the respective constructors
were called.
SomeClass::~SomeClass()
{
cout << "Destructor of
SomeClass" << endl;
}
operator=
- Overloading the assignment statement has nearly the same code as the copy constructor
-
Often the code inside the curly braces of operator= and the copy constructor
will be nearly identical.
SomeClass& operator=(SomeClass&
rValue);
Readings
Deitel and Deitel Fifth Edition, ??? .
Deitel and Deitel Fourth Edition, ??? .
| Back to Csc 125 Computer Science II, Programming in C++ |
| Scott Badman Office: B132 Phone: 353-2250 sbadman@parkland.edu |
Parkland College, 2400 W. Bradley Avenue, Champaign, IL 61821 |