Event Driven Programming: Operator Overloading
Basic Information
Different between override and overload
Override and overload are all about polymorphism.
Override | Overload | |
---|---|---|
Location | Superclass and Subclass | In the same class |
Feature | Must has same name, return value, parameter list as superclass | All function have the same name but different parameter list and return value |
Two ways to overloading operators
non-static member function | non-member function | |
---|---|---|
() ,[] ,-> and any assignment operator must be overload as class member |
one of the parameter must be an object class | |
Binary operator | with one parameter | with two parameter |
Unary operator | With no parameter | With one parameter |
The parameter is the operand beside the operator.
The function name must start as operator
, such as operator+()
,operator-()
and operator[]()
.
Other Attention
Three operator is exceptions:
- Assignment operator(
=
)- It default memberwise assignment for "source" to "target".
- If object has pointer member, that will be dangerous.
- Most of the time we will overloading assignment operator.
- Address operator(
&
)- It will return a pointer to the object.
- It also can be overload.
- Comma operator(
,
)- From left to right evaluation the expression and return the result that the last expression’s value.
Four operator are not be overloaded:.
| .*
| ::
| ?:
.
Three feature cannot be changed:
- Precedence of operator.
- Associativity of operator.
- The "arity" of operator.
- "arity" means arguments number.
- Binary just be overload as binary.
- Unary just be overload as unary.
Four special operator, they have both binary and unary version: &
, *
, +
, -
Attention:
- Cannot create new operator.
- Cannot change the meaning of how an operator works on values of fundamental types.
- Such as you cannot overload the
+
to implement the subtract(-
).
- Such as you cannot overload the
+
and+=
is two different operator, should be overload separately.
Simple Operator Overloading
Binary operator
non-static member function | non-member function |
---|---|
with one argument | with two argument |
Example
Here is an example about the operator +
and =
overloading by non-static member function .
#include <bits/stdc++.h>
using namespace std;
class mySecond
{
public:
int sec;
int usec;
mySecond operator+(mySecond const &other)
{
mySecond temp = *this;// copy object
temp.sec += other.sec;
temp.usec += other.usec;
return temp;// return the copy
}
// const is in order to enable the (a=b=c) but deny the (a=b)=c
const mySecond &operator=(mySecond const &other)
{
sec = other.sec;
usec = other.usec;
return *this;
}
};
int main()
{
mySecond A;
A.sec = 0;
A.usec = 100;
mySecond B;
B.sec = 100;
B.usec = 0;
A = A + B;
cout << A.sec << " " << A.usec << endl;
return 0;
}
Output:
100 100
If you want to overloading other operators, just follow this format.
But you need attention to the return value of overloading.
Processing of Overloading Invoke
Object a,b;
a=a+b;
As the code, it will be generate by compiler to:
a.operator=(a.operator=(b));
If overloading by non-member function:
operator=(a,operator+(a,b));
Return Value
Different operator have different return values, it has special purpose.
For example:
mySecond operator+(mySecond const &other)
const mySecond &operator=(mySecond const &other)
operator+
return a vairable by value, but operator=
return a const variable by reference.
As we all know, if you just add two value but not assignment, the result will not be stored, and the value of a
or b
are not change too.
int a=1,b=1;
a+b;
So, as the concrete realization, we copy the object at first. And then, all operations are done on this copy, we return the copy at the end.
For operator=
, the aim we use the =
is change the value of left operand, so we can directly change the value of object, instead of copying.
Benefits of return reference:
- Improve performance.
- Help cascaded invoke.(Such as stream operator).
- The result can be modified.
If you do not to change the result, just add a const
.(Like the operator=
).
Why we need non-member function?
As we all know, if the operator is overloaded, the expression will generate Object.operator+( rightOperand )
.
The Object
is left operand, so the program can invoke the member function in the Object
.
But when the left operand is fundamental type or the Object is not the same class with right operand, the non-member function is useful.
Consider this example:
Object temp;
temp = 10 + temp;
10
is a fundamental type constant, it don’t have the member function operator+()
, in this case the program will find the operator+()
in global scope.
If there no non-member function declared, the error will occured.
In short, all the operator overloading in left operand should be declared as non-member function.
Stream Operator is a typically example of non-member function.
Stream Operator Overloading
Stream insertion operator <<
and stream extraction operator >>
.
Some function in stream
Function name | setw() |
ignore() |
---|---|---|
Description | Limits the number of characters read into each string | Which discards the specified number of characters in the input stream |
The process of stream operator was used
If we overload the operator <<
and >>
, when the program use the cin>>object
, the compiler will generate the non-member functino call(cout<<
is same) :
operator>>(cin, object)
This function will return a istream
reference, so you can cascaded with other object.
Operand
If we use <<
>>
:
Left operand | Right operand |
---|---|
ostream & or istream & |
user-defined class |
friend keyword
friend
can make the non-member function access the private member of class.
friend function
should be declared in the class member function list (but friend function
is not member function).
In the stream operator, the left operand is cin
operator , the compiler will generate the non-member function operator>>(cin, object)
.
This is a global funciton, the function cannot access the private member of object
, if it access the private member, the program will occur the error.
In order to access the private member, we should declare as the friend function
.
But, if you through the accessor and mutator function to access the private member, the friend
is not necessary.
With friend
class mySecond
{
public:
mySecond(int a = 10, int b = 10)
{
sec = a;
usec = b;
}
friend ostream &operator<<(ostream &out, mySecond &other);
private:
int sec;
int usec;
};
ostream &operator<<(ostream &out, mySecond &other)
{
out << "sec=" << other.sec << "\nusec=" << other.usec << endl;
return out;
}
int main()
{
mySecond A(0, 100);
cout << A << endl;
return 0;
}
Without friend
class newSecond : public mySecond
{
public:
newSecond(int a = 10, int b = 10) : mySecond(a, b)
{
}
int getSec()
{
return mySecond::getSec();
}
int getUsec()
{
return mySecond::getUsec();
}
};
ostream &operator<<(ostream &out, newSecond &other)
{
out << "sec=" << other.getSec() << "\nusec=" << other.getUsec() << endl;
return out;
}
int main()
{
newSecond A(0, 100);
cout << A << endl;
return 0;
}
Unary operator
non-static member function | non-member function |
---|---|
with no argument | with one argument |
Must be an object (or reference of object) |
Using reference?
If parameter is reference, it will efffect the original object.
Prefix and Postfix with ++
and --
Prefix and postfix are allowed overload in the same class(at the same time), but you should use the distinct signature to recognize them.
Difference between prefix and postfix:
Prefix | Postfix | |
---|---|---|
As same as the othe unary operator | Have a convention | |
non-static member function | will generates d1.operator++() |
will generates d1.operator++(0) |
Prototype | Date &operator++() |
Date &operator++( int ) |
non-member function | will generate operator++(d1) |
will generates operator++( d1, 0 ) |
Prototype | Date operator++( Date & ) |
Date operator++( Date &, int ) |
Return value | return by reference | return by value |
The value is a temporary object that contains is the original value befor increment |
Example:
#include <bits/stdc++.h>
using namespace std;
class mySecond
{
public:
int sec;
int usec;
mySecond(int a = 10, int b = 10)
{
sec = a;
usec = b;
}
mySecond &operator++()
{
usec += 1;
return *this;
}
mySecond operator++(int)
{
mySecond temp = *this;
usec += 1;
return temp;
}
};
int main()
{
mySecond A(0, 100);
mySecond B(0, 100);
cout << (A++).usec << endl;
cout << A.usec << endl;
cout << (++B).usec << endl;
cout << B.usec << endl;
return 0;
}
Why the return value is different?:
As we all know, a++
is first use last increment, ++a
is first increment last use:
int a;
a=0;
int b=a++;
a=0
int c=++a;
The result of code is b=0,c=1
.
If we use the ++a
, we need return the value that is incremented, but a++
we need return the value before increment.
So in postfix, we copy the value be increment, and at last return it.
One sentence:
When we define the function, we should set different signature to distinguish the prefix and postfix.(postfix is one more parameter than prefix (a dummy value))
Attention:
- Postfix will create a temporary object, so it will cause the performance problem.
- When we overloading the operator, we should set the right return value.
- Prefix return a reference.
- Postfix return a value.
你小子 可以!