This is an intermediate conversion course for C++, suitable for second year computing students who may have learned Java or another language in first year.
2. Introduction
• Today’s lecture is about operator overloading.
• Here be dragons.
• This is not a facility of which I am very fond…
• … coverage in the module is provided for completeness.
• It is a facility that does not exist in Java or C#.
• It is very powerful, very useful, but one of the easiest
ways to develop virtually unreadable code.
3. Operator Overloading
• Operator overloading is the process of providing object
specific functionality for the base operators:
• +
• -
• /
• *
• You can overload these to allow you to, for example,
divide objects by each other or multiply them together.
• In Java if you wish to do this, you must define methods for the
operation.
4. The Structure of an Operator
• All operators, fundamentally, do two things:
• Operate on one or more values (known as operands)
• Return a value
• Consider the example:
• int num = 4 + 5
• Operator is +
• Operands and 4 and 5
• Returns the sum of the two operands - 9
5. Overloading an Operator
• In C++, an overloaded operator is just another method
defined in the class.
• As with a constructor, it has a specific naming convention.
• The method name is the keyword operator followed by
the operator symbol:
• operator+, as an example.
• These must be prototyped as usual in the class definition.
6. Overloaded Operator
class Employee {
private:
int payscale;
public:
int query_payscale();
void set_payscale (int p);
virtual bool can_hire_and_fire();
virtual bool has_authority (string);
int operator+ (int);
}
int Employee::operator+ (int num) {
payscale += num;
return payscale;
}
7. Limitations
• Overloaded operators will not work with pointers.
• They work only with value objects.
• If you want to use an overloaded operator on a pointer, you must
explicitly dereference it first.
• You can only redefine pointers for classes you write
yourself.
• The left hand operand is always the left hand side of the
operator.
• The right hand side can be any data type.
8. Applicability
• Almost any operator can be overloaded in C++.
• Including array notation and the new keyword.
• In most cases, the overloaded operators are just for
convenience.
• A syntactic nicety.
• Which comes with all sorts of problems.
• However, it is often necessary to overload the = operator.
9. Dynamic Data
• C++ allows us to pass by both reference and value.
• For objects and primitive data types.
• Java allows only pass by reference.
• What happens in C++ if we pass by value an object
containing pointers?
• It creates only a shallow copy of the object.
• It copies only the object’s data fields.
• The pointer in the copy will point to the original dynamic
memory.
10. Shallow Copies
• This is known as a shallow copy:
• Person a;
• Person b = a;
• Both a and b make use of the same dynamic data.
• C++ gives us two ways of dealing with this.
• Overloading the assignment operator
• Defining a copy constructor.
• They have the same basic intention.
• Assignment operators are used when an object already exists.
• Copy constructors are used when a new object must be created.
11. Copy Constructors
• There’s nothing syntactically special about a copy
constructor.
• It’s a constructor that takes a configured object as a parameter.
• This object must be passed by reference
• Where it differs is in how it works.
• In it you can handle the creation and manipulation of data fields as
you see fit.
12. Copy Constructors
• There are three point when a copy constructor will be
called.
• When an object is created from another object of the same type.
• When an object is passed by value as a parameter to a function.
• When an object is returned from a function.
• If a copy constructor is not defined, one will be created by
the compiler.
• This is fine if you’re not working with dynamic data.
13. Stack Example
class Stack {
private:
int size;
int *elements;
public:
void push(int);
int pop();
Stack();
};
#include "Stack.h"
Stack::Stack() {
elements = new int [100];
size = 0;
}
int Stack::pop() {
int popped;
if (size == 0) {
return -1;
}
popped = elements[size-1];
size -= 1;
return popped;
}
void Stack::push(int val) {
if (size == 100) {
return;
}
elements[size] = val;
size += 1;
}
15. Copy Constructor
class Stack {
private:
int size;
int *elements;
public:
void push(int);
int pop();
Stack();
Stack (Stack&);
};
Stack::Stack (Stack& copy) {
elements = new int[100];
for (int i = 0; i < copy.size; i++) {
elements[i] = copy.elements[i];
}
}
16. Copy Constructor
• This only works when we create an object from an
existing object:
• Stack my_second_stack = my_stack
• Not when we create an object afterwards:
• Stack my_second_stack;
• my_second_stack = my_stack
• For the latter case, we must provided an overloaded
assignment operator.
17. Overloaded =
class Stack {
private:
int size;
int *elements;
public:
void push(int);
int pop();
Stack();
Stack (Stack&);
Stack operator= (Stack&);
};
Stack Stack::operator =(Stack& copy) {
size = copy.size;
elements = new int[100];
for (int i = 0; i < size; i++) {
elements[i] = copy.elements[i];
}
return (*this);
}
18. Notes on Both Approaches
• You can access private data members of the parameter
passed in.
• this in c++ used to provide a reference to the object in
which the code is defined.
• Used to return a reference to the object.
• Code suffers from memory leaks!
• Must explicitly delete dynamic memory before assigning new
objects.
19. Deep Copies
• In both cases, the two approaches are used to provide
deep copies of an object.
• We don’t just copy a pointer reference, we copy the contents being
pointed to.
• This ensures that our references are clean and not
overlapping.
• These kind of errors are extremely subtle.
• Need to do this when dynamic data is being stored.
20. Copy versus Assignment
• Copy constructors:
• Create a new object
• By copying an old object
• Called when objects are passed or returned by value.
• Like all constructors, does not return a value.
• Overloaded =:
• Copies an existing object onto another existing object.
• Returns a reference to the newly setup object.
• When working with dynamic data, you need to provide both!
21. Summary
• You can overload operators in C++
• But you know… don’t.
• The only time this is needed and appropriate is
overloading the equals operator.
• Shallow copies of objects cause side-effects.
• And these side-effects are often very difficult to detect.
• Deep copies using copy constructors and overloaded
operators are required.