What are the benefits of operator overloading?
By overloading standard operators on a class, you can exploit the intuition of the users of that class. This lets users program in the language of the problem domain rather than in the language of the machine.
The ultimate goal is to reduce both the learning curve and the defect rate
What's the deal with operator overloading?
It allows you to provide an intuitive interface to users of your class, plus makes it possible for templates to work equally well with classes and built-in/intrinsic types.
Operator overloading allows C/C++ operators to have user-defined meanings on user-defined types (classes). Overloaded operators are syntactic sugar for function calls:
class Fred {
public:
...
};
#if 0
// Without operator overloading:
Fred add(const Fred& x, const Fred& y);
Fred mul(const Fred& x, const Fred& y);
Fred f(const Fred& a, const Fred& b, const Fred& c)
{
return add(add(mul(a,b), mul(b,c)), mul(c,a)); // Yuk...
}
#else
// With operator overloading:
Fred operator+ (const Fred& x, const Fred& y);
Fred operator* (const Fred& x, const Fred& y);
Fred f(const Fred& a, const Fred& b, const Fred& c)
{
return a*b + b*c + c*a;
}
#endif
---------------------------------------------------------------------------------------------------------------------
What are some examples of operator overloading?
Here are a few of the many examples of operator overloading:
myString + yourString might concatenate two std::string objects
myDate++ might increment a Date object
a * b might multiply two Number objects
a[i] might access an element of an Array object
x = *p might dereference a "smart pointer" that "points" to a disk record it could seek to the location on disk where p "points" and return the appropriate record into x
---------------------------------------------------------------------------------------------------------------------
What operators can/cannot be overloaded?
Most can be overloaded. The only C operators that can't be are . and ?: (and sizeof, which is technically an operator). C++ adds a few of its own operators, most of which can be overloaded except :: and .*.
Here's an example of the subscript operator (it returns a reference). First without operator overloading:
class Array {
public:
int& elem(unsigned i) { if (i > 99) error(); return data[i]; }
private:
int data[100];
};
int main()
{
Array a;
a.elem(10) = 42;
a.elem(12) += a.elem(13);
...
}
Now the same logic is presented with operator overloading:
class Array {
public:
int& operator[] (unsigned i) { if (i > 99) error(); return data[i]; }
private:
int data[100];
};
int main()
{
Array a;
a[10] = 42;
a[12] += a[13];
...
}
---------------------------------------------------------------------------------------------------------------------
Can I overload operator== so it lets me compare two char[] using a string comparison?
No: at least one operand of any overloaded operator must be of some user-defined type (most of the time that means a class).
But even if C++ allowed you to do this, which it doesn't, you wouldn't want to do it anyway since you really should be using a std::string-like class rather than an array of char in the first place since arrays are evil.
---------------------------------------------------------------------------------------------------------------------
Can I create a operator** for "to-the-power-of" operations?
Nope.
The names of, precedence of, associativity of, and arity of operators is fixed by the language. There is no operator** in C++, so you cannot create one for a class type.
If you're in doubt, consider that x ** y is the same as x * (*y) (in other words, the compiler assumes y is a pointer). Besides, operator overloading is just syntactic sugar for function calls. Although this particular syntactic sugar can be very sweet, it doesn't add anything fundamental. I suggest you overload pow(base,exponent) (a double precision version is in ).
By the way, operator^ can work for to-the-power-of, except it has the wrong precedence and associativity.
---------------------------------------------------------------------------------------------------------------------
How do I create a subscript operator for a Matrix class?
Use operator() rather than operator[].
When you have multiple subscripts, the cleanest way to do it is with operator() rather than with operator[]. The reason is that operator[] always takes exactly one parameter, but operator() can take any number of parameters (in the case of a rectangular matrix, two parameters are needed).
For example:
class Matrix {
public:
Matrix(unsigned rows, unsigned cols);
double& operator() (unsigned row, unsigned col); © subscript operators often come in pairs
double operator() (unsigned row, unsigned col) const; © subscript operators often come in pairs
...
~Matrix(); // Destructor
Matrix(const Matrix& m); // Copy constructor
Matrix& operator= (const Matrix& m); // Assignment operator
...
private:
unsigned rows_, cols_;
double* data_;
};
inline
Matrix::Matrix(unsigned rows, unsigned cols)
: rows_ (rows)
, cols_ (cols)
//data_ <--initialized below (after the 'if/throw' statement)
{
if (rows == 0 || cols == 0)
throw BadIndex("Matrix constructor has 0 size");
data_ = new double[rows * cols];
}
inline
Matrix::~Matrix()
{
delete[] data_;
}
inline
double& Matrix::operator() (unsigned row, unsigned col)
{
if (row >= rows_ || col >= cols_)
throw BadIndex("Matrix subscript out of bounds");
return data_[cols_*row + col];
}
inline
double Matrix::operator() (unsigned row, unsigned col) const
{
if (row >= rows_ || col >= cols_)
throw BadIndex("const Matrix subscript out of bounds");
return data_[cols_*row + col];
}
Then you can access an element of Matrix m using m(i,j) rather than m[i][j]:
int main()
{
Matrix m(10,10);
m(5,8) = 106.15;
std::cout << m(5,8);
...
}
---------------------------------------------------------------------------------------------------------------------
How can I overload the prefix and postfix forms of operators ++ and --?
Via a dummy parameter.
Since the prefix and postfix ++ operators can have two definitions, the C++ language gives us two different signatures. Both are called operator++(), but the prefix version takes no parameters and the postfix version takes a dummy int. (Although this discussion revolves around the ++ operator, the -- operator is completely symmetric, and all the rules and guidelines that apply to one also apply to the other.)
class Number {
public:
Number& operator++ (); // prefix ++
Number operator++ (int); // postfix ++
};
Note the different return types: the prefix version returns by reference, the postfix version by value. If that's not immediately obvious to you, it should be after you see the definitions (and after you remember that y = x++ and y = ++x set y to different things).
Number& Number::operator++ ()
{
...
return *this;
}
Number Number::operator++ (int)
{
Number ans = *this;
++(*this); // or just call operator++()
return ans;
}
The other option for the postfix version is to return nothing:
class Number {
public:
Number& operator++ ();
void operator++ (int);
};
Number& Number::operator++ ()
{
...
return *this;
}
void Number::operator++ (int)
{
++(*this); // or just call operator++()
}
However you must *not* make the postfix version return the 'this' object by reference; you have been warned.
Here's how you use these operators:
Number x = /* ... */;
++x; // calls Number::operator++(), i.e., calls x.operator++()
x++; // calls Number::operator++(int), i.e., calls x.operator++(0)
Assuming the return types are not 'void', you can use them in larger expressions:
Number x = /* ... */;
Number y = ++x; // y will be the new value of x
Number z = x++; // z will be the old value of x
Which is more efficient: i++ or ++i?
++i is sometimes faster than, and is never slower than, i++.
For intrinsic types like int, it doesn't matter: ++i and i++ are the same speed. For class types like iterators or the previous FAQ's Number class, ++i very well might be faster than i++ since the latter might make a copy of the this object.
The overhead of i++, if it is there at all, won't probably make any practical difference unless your app is CPU bound. For example, if your app spends most of its time waiting for someone to click a mouse, doing disk I/O, network I/O, or database queries, then it won't hurt your performance to waste a few CPU cycles. However it's just as easy to type ++i as i++, so why not use the former unless you actually need the old value of i.
So if you're writing i++ as a statement rather than as part of a larger expression, why not just write ++i instead? You never lose anything, and you sometimes gain something. Old line C programmers are used to writing i++ instead of ++i. E.g., they'll say, for (i = 0; i < 10; i++) .... Since this uses i++ as a statement, not as a part of a larger expression, then you might want to use ++i instead. For symmetry, I personally advocate that style even when it doesn't improve speed, e.g., for intrinsic types and for class types with postfix operators that return void.
Obviously when i++ appears as a part of a larger expression, that's different: it's being used because it's the only logically correct solution, not because it's an old habit you picked up while programming in C.
---------------------------------------------------------------------------------------------------------------------
No comments:
Post a Comment