a = b*c + d;where all variables are matrices than this:
Matrix res(b);
res.multiply(c);
res.plus(d);
a.set(res);
There are some limitations in operator overloading:
| Operator | Meaning |
|---|---|
| . | member selection |
| :: | scope resolution operator |
| sizeof | |
| .* | member dereference |
| ?: | conditional operator |
| Overloadable operators | ||||||
|---|---|---|---|---|---|---|
| + | - | * | / | % | ^ | & |
| | | ~ | ! | = | < | > | += |
| -= | *= | /= | %= | ^= | &= | |= |
| << | >> | >>= | <<= | == | != | <= |
| >= | && | || | ++ | -- | ->* | , |
| -> | [] | () | new | new[] | delete | delete[] |
Polynomial operator+(const Polynomial& p, const Polynomial& q)
{
int max_degree = p.degree>q.degree ? p.degree : q.degree;
Polynomial res( max_degree );
for(int i=max_degree;i>=0;i--)
res.a[i] = (i<=p.degree?p.a[i]:0) + (i<=q.degree?q.a[i]:0);
return res;
}
where the class Polynomial can be defined as
class Polynomial {
private:
int degree;
int *a; // coefficients
public:
Polynomial();
Polynomial(int d);
virtual ~Polynomial() { delete[] a; };
// overloading operators
friend Polynomial operator+(const Polynomial& p, const Polynomial& q);
};
Polynomial::Polynomial() : degree(0)
{
a = new int[1];
a[0] = 0;
}
// creates polynomial "x^d"
Polynomial::Polynomial(int d)
{
degree = d>=0 ? d : 0;
a = new int[degree+1];
a[degree] = 1;
for(int i=0;i<degree;i++)
a[i] = 0;
}
please note that in the class definition we specified the stand alone function overloading operator as a friend
of the class to provide access to private variables. After overloading operator this way the following code
a = p + q;where all variables are of the Polynomial type will be executed as:
a = operator+(p, q);We also would like to note that not all operators can be overloaded as separate functions. For example, assignment operators, function call operator, index operator cannot be.
Another way to overload an operator is to define the function overloading an operator as a member of the class. Here is an example that overloads operator - (minus) for the same class:
class Plynomial {
...
public:
...
Polynomial operator-(const Polynomial& q) const;
};
Polynomial Polynomial::operator-(const Polynomial& q) const
{
int max_degree = degree>q.degree ? degree : q.degree;
Polynomial res( max_degree );
for(int i=max_degree;i>=0;i--)
res.a[i] = (i<=degree?a[i]:0) - (i<=q.degree?q.a[i]:0);
return res;
}
If we overloaded the subtraction operator as a class member, the following code
a = b - c;is taken as
a = b.operator-(c);Please note that this operator does not change the value of the Polynomial b, instead it creates a new Polynomial res equal to the difference between b and c and returns it as the value.
The essential difference between member operators and function operators is that member operator is considered as object.operator( parameter ). That is, the first argument always has to be an object of the class we overload this operator for. For example, we can easily overload operator * to multiply a polynomial by an integer number
Polynomial Polynomial::operator*(int k)
{
Polynomial res = *this;
for(int i=0;i<=degree;i++) res.a[i] *= k;
return res;
}
but we cannot overload similarly overload operator * to multiply an integer number by a polynomial,
because the code i * p will be treated as
i.operator*(p)where i is an integer variable. In such cases we need to overload these operators as separate functions:
Polynomial operator*(int k, const Polynomial& p)
{
Polynomial res = p;
for(int i=0;i<=res.degree;i++) res.a[i] *= k;
return res;
}
Polynomial& Polynomial::operator=(const Polynomial& p)
{
delete[] a; // we are changing the value of the current polynomial => clear the memory
degree = p.degree;
a = new int[degree+1];
for(int i=0;i<=degree;i++) a[i] = p.a[i];
return *this;
}
Now, the same code
a = b + c;will be considered by the compiler as
a.operator=( operator+( b, c) );Please note that the assignment operator return the reference to the object invoking the operator. It is done to be able to write code like a = b = c.
To overload a compound assignment operator like += we do almost the same:
Polynomial& Polynomial::operator+=(const Polynomial& p)
{
int i;
if( degree < p.degree ){
int *t = new int[p.degree+1];
for(i=0;i<=p.degree;i++)
t[i] = (i<=degree ? a[i] : 0);
degree = p.degree;
delete[] a;
a = t;
}
for(i=0;i<=degree;i++) a[i] += p.a[i];
return *this;
}
Polynomial operator-(const Polynomial& p)
{
Polynomial res = p;
for(int i=0;i<=res.degree;i++)
res.a[i] = - res.a[i];
return res;
}
to overload it as a member operator we need to do this
Polynomial Polynomial::operator-()
{
Polynomial res = *this;
for(int i=0;i<=res.degree;i++)
res.a[i] = - res.a[i];
return res;
}
Polynomial& Polynomial::operator++()
{
a[0]++;
return *this;
}
Please note that this operator returns a reference to the object invoking the operator. This allows users to put this
operator on the left side of an assignment operator.
To overload a postfix operator, we need to declare a function that takes one dummy argument (which we won't be using) to distinguish it with a prefix form:
Polynomial Polynomial::operator++(int dummy)
{
Polynomial res = *this;
a[0]++;
return res;
}
Note that to keep the usual meaning of the operator (first return the old value and then increment) we need to store
the current value and then return it. Because of this, we return a value not a reference.
After the overloading the following code:
p++; ++p;will be considered as
p.operator++( 0 /* dummy argument */); p.operator++( );
Polynomial p(3); cout<<p;will print the polynomial p to the standard output. To do this, we need to overload the shift operator << to make the operator work with the left argument of the ostream type and the right argument of the Polynomial type. Obviously, this operator has to be overloaded as a separate function:
ostream& operator<<(ostream& out, const Polynomial& p)
{
for(int i=p.degree; i>=0; i++){
if( p.a[i] == 0 ) continue;
out<<(i<p.degree&&p.a[i]>0?"+":"")<<p.a[i];
if( i > 0 ){
out<<"*x";
if( i > 1 ) out<<"^"<<i;
}
}
return out;
}
please note that we return the left argument as the value of the function to be able to use the operator in a sequence:
cout<<"p = "<<p<<endl;We can similarly overload the input operator, but since the code will be more complicated we decided to demonstrate only the declaration:
class Polynomial {
...
public:
...
friend ostream& operator<<(ostream& out, const Polynomial& p);
friend istream& operator>>(istream& inp, const Polynomial& p);
};
int Polynomial::operator[](int index)
{
if( index<0 || index>degree )
throw out_of_range("index in the Polynomial [] operator is out of range");
return a[index];
}
please note that our method returns an integer value. By doing so we allow programmers read-only access to the coefficients.
That is, they can do this:
cout<<p[4];but they cannot do this
p[5] = 4;In order to provide the read-write access to the coefficients we need to return not an integer but a reference to the integer value:
int& Polynomial::operator[](int index)
{
if( index<0 || index>degree )
throw out_of_range("index in the Polynomial [] operator is out of range");
return a[index];
}
However, providing such an access may cause problems with the degree of polynomials. Since we try to keep the highest
degree coefficient non zero, and letting users write access to it is not wise.
If we redefine the function call operator, we will be able to do something like this:
cout<<"p(3) = "<<p(3);Of course, in this case it makes sense to define the () operator in such a way that it computes the value of the polynomial at the given point. Here is an example:
double Polynomial::operator()(double x) const
{
double res = 0;
for(int i=degree; i>=0; i++)
res = res * x + a[i];
return res;
}
This operator has one interesting feature: this is the only operator we can change the number of arguments for. In other words,
we can overload the function call operator and make it take two or three arguments. This feature can be useful if we create
a class that implements a multidimensional array. Unfortunately, we are not allowed to change the number of arguments for
the subscript operator. That is, this marray[1, 3] would be a wrong construct, but we can change the number
of arguments for the () operator, so we can use marray(1, 3) instead.
#include <iostream>
#include "polynomial.h"
using namespace std;
void main()
{
Polynomial x(1), x2(2), x3(3), p;
p = 2*x3 - 3*x2 + x;
cout<<p<<"\n";
p = p + 4;
cout<<p<<"\n";
}
In line 8 we use the multiplication operator, addition operator, and subtraction operator. After this line, the value of the
polynomial p becomes To answer this question we need to understand what happens when a compiler sees the line
p = p + 4;Since we did not overload operator + that takes one polynomial argument and one integer argument, the program has to use operator plus that takes two polynomial arguments. But the second argument is not a polynomial, in order to make it a polynomial we need to convert an integer into a polynomial. Since we have a constructor that takes only one integer, the compiler will implicitly use this constructor to perform the conversion. Thus, the compiler takes this instruction as:
p.operator=( operator+( p, Polynomial(4)) );But the constructor Polynomial(4) actually creates polynomial x4 and this explains the strange result of our program.
One way to solve a problem like this is to prohibit the compiler implicitly execute constructors. In order to do so, we need to use keyword explicit in front of a constructor name. For example, if in the class definition we have:
class Polynomial {
private:
...
public:
Polynomial() ... ;
explicit Polynomial(int d) ... ;
...
};
then during the compilation time we would receive an error message that operator + (Polynomial, int) is not defined.
Of course, to be able to perform the addition we still need to overload operator plus for Polynomial and
integer, but now we now how to do that. Don't we?
If we consider the code like
{
Robot *robot[10];
for(int i=0;i<10;i++)
robot[i] = new Robot();
}
then we will execute the constructor for the Robot class 10 times, but we don't execute a destructor even once
despite the fact that our pointers are no longer exist once we left the {} block. You can study the
example program to see it. This happens, because
we don't actually declared objects in a block, we just declared pointers. Then we allocated memory for the objects, but
lost pointers to the memory. This behavior is not always appropriate and may result in wasting resources. In order to
avoid such a behavior we will develop a new smart pointers. An object of the new RobotPtr class will contain
two pieces of information:
class RobotPtr {
private:
Robot *ptr;
int counter;
public:
...
};
we will run into a problem, ho to update the counter inside all pointers to a particular instance of the Robot
class, when we create or destroy an smart pointer to the very same instance. Shortly speaking we want all instances
of the RobotPtr class that point to the same Robot object share the counter. To achieve
this, we will declare a counter field as a pointer to an integer. Thus, the complete definition of the
new class could look like:
class RobotPtr {
private:
Robot *ptr;
int *counter;
public:
RobotPtr(Robot *p=NULL);
~RobotPtr();
RobotPtr& operator=(const RobotPtr& p);
const RobotPtr& operator*();
Robot* operator->();
};
You can find the complete implementation on the example page.