Sie sind auf Seite 1von 41

Chapter 10: Operator Overloading

_________________________________________________________________________________________________________
Consider the following C++ code snippet:
vector<string> myVector(kNumStrings);
for(vector<string>::iterator itr = myVector.begin();
itr != myVector.end(); itr)
!itr = "No# $onger!";
Here, we create a vector<string> of a certain size, then iterate over it concatenating Now longer! to
each of the strings. Code like this is ui!uitous in C++, and initiall" does not appear all that e#citing. How$
ever, let%s take a closer look at how this code is structured. &irst, let%s look at e#actl" what operations
we%re perfor'ing on the iterator:
vector<string> myVector(kNumStrings);
for(vector<string>::iterator itr = myVector.begin();
itr != myVector.end(); itr)
!itr = "No# $onger!";
(n this si'ple piece of code, we%re co'paring the iterator against myVector.end() using the != operator,
incre'enting the iterator with the operator, and dereferencing the iterator with the ! operator. )t a
high level, this doesn%t see' all that out of the ordinar", since *+, iterators are designed to look like regu$
lar pointers and these operators are all well$defined on pointers. -ut the ke" thing to notice is that *+,
iterators aren't pointers, the"%re objects, and !=, !, and aren%t nor'all" defined on o.ects. /e can%t
write code like myVector or !my%a& = '(), so wh" can these operations e applied to
vector<string>::iterator0
*i'ilarl", notice how we%re concatenating the string Now longer! onto the end of the string:
vector<string> myVector(kNumStrings);
for(vector<string>::iterator itr = myVector.begin();
itr != myVector.end(); itr)
!itr = "No# $onger!";
1espite the fact that string is an o.ect, so'ehow C++ knows what it 'eans to appl" = to strings.
)ll of the aove e#a'ples are instances of operator overloading, the ailit" to specif" how operators nor$
'all" applicale to pri'itive t"pes can interact with custo' classes. 2perator overloading is ui!uitous in
professional C++ code and, used correctl", can 'ake "our progra's 'ore concise, 'ore readale, and
'ore te'plate$friendl".
+here are two overarching purposes of operator overloading. &irst, operator overloading enales "our
custo' classes to act like pri'itive t"pes. +hat is, if "ou have a class like vector that 'i'ics a standard
C++ arra", "ou can allow clients to use arra" notation to access individual ele'ents. *i'ilarl", when
designing a class encapsulating a 'athe'atical entit" 3for e#a'ple, a co'ple# nu'er4, "ou can let clients
appl" 'athe'atical operators like , *, and ! to "our t"pe as though it were uilt into the language.
*econd, operator overloading enales "our code to interact correctl" with te'plate and lirar" code. &or
e#a'ple, "ou can overload the << operator to 'ake a class co'patile with the strea's lirar", or the <
operator to interface with *+, containers.
$ 567 $ Chapter 10: Operator Overloading
+his chapter discusses general topics in operator overloading, de'onstrating how to overload so'e of the
'ore co''on operators. (t also includes tricks and pitfalls to e aware of when overloading certain oper$
ators.
A Word of Warning
( would e re'iss to discuss operator overloading without first prefacing it with a warning: operator over$
loading is a doule$edged sword. /hen used correctl", operator overloading can lead to intuitive, te'$
plate$friendl" code that elegantl" perfor's co'ple# operations ehind the scenes. However, incorrectl"
overloaded operators can lead to incredil" sutle ugs. *ee'ingl" innocuous code along the lines of
!my+tr = , can cause serious prole's if i'ple'ented incorrectl", and without a solid understanding of
how to overload operators "ou 'a" find "ourself in serious troule.
+here is a pearl of design wisdo' that is particularl" applicale to operator overloading:
The Principle of Least Astonishment: ) function%s na'e should co''unicate its ehavior and should e
consistent with other na'ing conventions and idio's.
+he principle of least astonish'ent should e fairl" ovious 8 "ou should design functions so that clients
can understand what those functions do si'pl" " looking at the functions% na'es9 that is, clients of "our
code should not e astonished that a function with one na'e does so'ething entirel" different. &or e#$
a'ple, a function na'ed -oSomet.ing violates the principle of least astonish'ent ecause it doesn%t
co''unicate what it does, and a function called /om&ute0rimes that reads a grocer" list fro' a file viol$
ates the principle ecause the na'e of the function is co'pletel" different fro' the operation it perfor's.
However, other violations of the principle of least astonish'ent are not as latant. &or e#a'ple, a custo'
container class whose em&ty 'e'er function erases the contents of the container violates the principle
of least astonish'ent ecause C++ progra''ers e#pect em&ty to 'ean is the container e'pt"0 rather
than e'pt" the container. *i'ilarl", a class that is itwise const ut not se'anticall" const violates the
principle, since progra''ers assu'e that o.ects can%t e 'odified " const 'e'er functions.
/hen working with operator overloading, it is crucial to adhere to the principle of least astonish'ent. C+
+ lets "ou redefine al'ost all of the uilt$in operators however "ou choose, 'eaning that it%s possile to
create code that does so'ething co'pletel" different fro' what C++ progra''ers 'ight e#pect. &or e#$
a'ple, suppose that "ou are working on a group pro.ect and that one of "our tea''ates writes a class
/ustomVector that acts like the *+, vector ut which perfor's so'e additional operations ehind the
scenes. :our progra' contains a s'all ug, so "ou look over "our tea''ate%s code and find the following
code at one point:
/ustomVector one = 1! ... !12 t#o = 1! ... !1;
one 3= t#o;
/hat does the line one 3= t#o do0 *"ntacticall", this sa"s take the re'ainder when dividing one "
t#o, then store the result ack in one. -ut this 'akes no sense 8 how can "ou divide one /ustomVector
" another0 :ou ask "our tea''ate aout this, and he infor's "ou that the 3= operator 'eans re'ove
all ele'ents fro' one that are also in t#o. +his is an egregious violation of the principle of least astonish$
'ent. +he code neither co''unicates what it does nor adheres to e#isting convention, since the se$
'antics of the 3= operator are 'eaningless when applied to linear data structures. +his is not to sa", of
course, that having the ailit" to re'ove all ele'ents fro' one /ustomVector that are contained in an$
other is a ad idea 8 in fact, it can e !uite useful 8 ut this functionalit" should e provided " a properl"$
na'ed 'e'er function rather than a cr"pticall"$overloaded operator. &or e#a'ple, consider the follow$
ing code:
Chapter 10: Operator Overloading $ 56; $
/ustomVector one = 1! ... !12 t#o = 1! ... !1;
one.remove4$$+n(t#o);
+his code acco'plishes the sa'e task as the aove code, ut does so " e#plicitl" indicating what opera$
tion is eing perfor'ed. +his code is 'uch less likel" to confuse readers and is far 'ore descriptive than
efore.
)s another e#a'ple, suppose that "our tea''ate also i'ple'ents a class called /ustomString that acts
like the standard string t"pe, ut provides additional functionalit". :ou write the following code:
/ustomString one = "5e$$o"2 t#o = " 6or$d";
one = t#o;
cout << one "!" << end$;
(ntuitivel", this should create two strings, then concatenate the second onto the end of the first. Ne#t, the
code prints out one followed " an e#cla'ation point, "ielding Hello /orld! <nfortunatel", when "ou
co'pile this code, "ou get an unusual error 'essage 8 for so'e reason the code one = t#o co'piles
correctl", ut the co'piler re.ects the code one "!". (n other words, "our tea''ate has 'ade it legal
to concatenate two strings using =, ut not " using . )gain, think ack to the principle of least astonish$
'ent. =rogra''ers tacitl" e#pect that o.ects that can e added with can e added with = and vice$
versa, and providing onl" half of this functionalit" is likel" to confuse code clients.
+he 'oral of this stor" is si'ple: when overloading operators, 'ake sure that "ou adhere to e#isting con$
ventions. (f "ou don%t, "ou will end up with code that is either incorrect, confusing, or oth.
Hopefull" this gri' introduction has not discouraged "ou fro' using operator overloading. 2perator
overloading is e#traordinaril" useful and "ou will not e disappointed with the possiilities that are aout
to open up to "ou. /ith that said, let%s egin discussing the 'echanics ehind this powerful techni!ue.
Defining Overloaded Operators
/e introduced operator overloading as a 'echanis' for redefining how the uilt$in operators appl" to
custo' classes. *"ntacticall", how do we co''unicate to the C++ co'piler that we want to redefine these
operators0 +he answer is so'ewhat odd. Here%s the original 'otivating e#a'ple we had at the eginning
of the chapter:
vector<string> myVector(kNumStrings);
for(vector<string>::iterator itr = myVector.begin();
itr != myVector.end(); itr)
!itr = "No# $onger!";
/hen "ou provide this code to the C++ co'piler, it interprets it as follows:
vector<string> myVector(kNumStrings);
for(vector<string>::iterator itr = myVector.begin();
o&erator!= (itr2 myVector.end());
itr.o&erator())
(itr.o&erator!()).o&erator =("No# $onger!");
Notice that ever"where we used the uilt$in operator in con.unction with an o.ect, the co'piler reinter$
preted the operator as a call to a speciall"$na'ed function called o&erator op, where op is the particular
operator we used. +hus itr != myVector.end() translated into o&erator!= (itr2
myVector.end()), itr was interpreted as itr.o&erator(), etc. )lthough these functions 'a"
have cr"ptic na'es, the"%re .ust regular functions. 2perator overloading is si'pl" syntax sugar, a wa" of
$ 565 $ Chapter 10: Operator Overloading
rewriting one operation 3in this case, function calls4 using a different s"nta# 3here, the uilt$in operators4.
2verloaded operators are not so'ehow 'ore efficient than other functions si'pl" ecause the function
calls aren%t e#plicit, nor are the" treated an" different fro' regular functions. +he" are special onl" in that
the" can are invoked using the uilt$in operators rather than through an e#plicit function calls.
(f "ou%ll notice, so'e of the operators used aove were translated into 'e'er function calls 3particularl"
and !4, while others 3!=4 were translated into calls to free functions. /ith a few e#ceptions, an" oper$
ator can e overloaded as either a free function or a 'e'er function. 1eter'ining whether to use free
functions or 'e'er functions for overloaded operators is a it trick", and we%ll discuss it 'ore as we con$
tinue our tour of overloaded operators.
>ach of C++%s uilt$in operators has a certain nu'er of operands. &or e#a'ple, the plus operator 3a b4
has two operands corresponding to the values on the left$ and right$hand sides of the operator. +he point$
er dereference operator 3!itr4, on the other hand, takes in onl" one operand: the pointer to dereference.
/hen defining a function that is an overloaded operator, "ou will need to ensure that "our function has
one para'eter for each of the operator%s operands. &or e#a'ple, suppose that we want to define a t"pe
7ationa$Number which encapsulates a rational nu'er 3a ratio of two integers4. -ecause it%s 'athe'at $
icall" sound to add two rational nu'ers, we 'ight want to consider overloading the operator as ap$
plied to 7ationa$Number so that we can add 7ationa$Numbers using an intuitive s"nta#. /hat would
such a function look like0 (f we i'ple'ent the operator as a 'e'er function of 7ationa$Number, the
s"nta# would e as follows:
c$ass 7ationa$Number 8
&ub$ic:
const 7ationa$Number o&erator (const 7ationa$Number9 r.s) const;
1! ... etc. ... !1
:;
3:ou 'ight e wondering wh" the return t"pe is const 7ationa$Number. &or now, "ou can ignore that...
we%ll pick this up in the ne#t section4
/ith o&erator defined this wa", then addition of 7ationa$Numbers will e translated into calls to the
'e'er function o&erator on 7ationa$Number. &or e#a'ple, the following code:
7ationa$Number one2 t#o;
7ationa$Number t.ree = one t#o;
will e interpreted " the co'piler as
7ationa$Number one2 t#o;
7ationa$Number t.ree = one.o&erator (t#o);
Notice that the code one t#o was interpreted as one.o&erator (t#o). +hat is, the receiver o.ect of
the o&erator function is the left$hand operand, while the argu'ent is the right$hand argu'ent. +his is
not a coincidence, and in fact C++ will guarantee that the relative ordering of the operands is preserved.
one t#o will never e interpreted as t#o.o&erator (one) under an" circu'stance, and our i'ple$
'entation of o&erator can take this into account.
)lternativel", we could consider i'ple'enting o&erator as a free function taking in two argu'ents. (f
we chose this approach, then the interface for 7ationa$Number would e as follows:
Chapter 10: Operator Overloading $ 56? $
c$ass 7ationa$Number 8
&ub$ic:
1! ... etc. ... !1
:;
const 7ationa$Number o&erator (const 7ationa$Number9 $.s2
const 7ationa$Number9 r.s);
(n this case, the code
7ationa$Number one2 t#o;
7ationa$Number t.ree = one t#o;
would e interpreted " the co'piler as
7ationa$Number one2 t#o;
7ationa$Number t.ree = o&erator (one2 t#o);
)gain, the relative ordering of the para'eters is guaranteed to e stale, and so "ou can assu'e that the
first para'eter to o&erator will alwa"s e on the left$hand side of the operator.
(n so'e cases, two operators are s"ntacticall" identical ut have different 'eanings. &or e#a'ple, the *
operator can refer either to the inar" sutraction operator 3a * b4 or the unar" negation operator 3*a4.
(f overload the * operator, how does the co'piler know whether "our overloaded operator is the unar" or
inar" version of *0 +he answer is rather straightforward: if the function operates on two pieces of data,
the co'piler treats o&erator* as the inar" sutraction operator, and if the function uses .ust one piece
of data it%s considered to e the unar" negation operator. ,et%s 'ake this discussion a it 'ore concrete.
*uppose that we want to i'ple'ent sutraction on the 7ationa$Number class. -ecause the inar" su$
traction operator has two operands, we would provide sutraction as an overloaded operator either as a
free function:
const 7ationa$Number o&erator* (const 7ationa$Number9 $.s2
const 7ationa$Number9 r.s);
or, alternativel", as a 'e'er function:
c$ass 7ationa$Number 8
&ub$ic:
const 7ationa$Number o&erator* (const 7ationa$Number9 r.s) const;
1! ... etc. ... !1
:;
Notice that oth of these functions operate on two pieces of data. (n the first case, the function takes in
two para'eters, and in the second, the receiver o.ect is one piece of data and the para'eter is the other.
(f we now want to provide an i'ple'entation of o&erator* which represents the unar" negation operat$
or, we could i'ple'ent it as a free function with the following signature:
const 7ationa$Number o&erator* (const 7ationa$Number9 arg);
2r as a 'e'er function of this for':
$ 56@ $ Chapter 10: Operator Overloading
c$ass 7ationa$Number 8
&ub$ic:
const 7ationa$Number o&erator* () const;

1! ... etc. ... !1
:;
)gain, don%t worr" aout wh" the return t"pe is a const 7ationa$Number. /e%ll address this shortl".
What Operator Overloading Cannot Do
/hen overloading operators, "ou cannot define rand$new operators like ; or <. )fter all, C++ wouldn%t
know the associativit" or proper s"nta# for the operator 3is one ; t#o t.ree interpreted as (one ;
t#o) t.ree or one ; (t#o t.ree)04 )dditionall", "ou cannot overload an" of the following oper$
ators:
Operator Syntax Name
:: %y/$ass::va$ue
*cope resolution
. one.va$ue
Ae'er selection
=: a > b = *' : '
+ernar" conditional
.! a.!my/$ass0tr;
=ointer$to$'e'er selection 3e"ond the scope of this te#t4
si>eof si>eof(%y/$ass)
*ize of o.ect
ty&eid ty&eid(%y/$ass)
Bunti'e t"pe infor'ation operator
(?)
static@cast
dynamic@cast
reinter&ret@cast
const@cast
(int) my/$ass;
+"pecast
Note that operator overloading onl" lets "ou define what uilt$in operators 'ean when applied to objects.
:ou cannot use operator overloading to redefine what addition 'eans as applied to ints, nor can "ou
change how pointers are dereferenced. +hen again, " the principle of least astonish'ent, "ou wouldn%t
want to do this an"wa".
Lvalues and Rvalues
-efore we egin e#ploring so'e of the i'ple'entation issues associated with overloaded operators, we
need to take a !uick detour to e#plore two concepts fro' progra''ing language theor" called lvalues and
rvalues. ,values and rvalues stand for left values and right values are are so$na'ed ecause of where
the" can appear in an assign'ent state'ent. (n particular, an lvalue is a value that can e on the left$hand
side of an assign'ent, and an rvalue is a value that can onl" e on the right$hand side of an assign'ent.
&or e#a'ple, in the state'ent A = ,, A is an lvalue and , is an rvalue. *i'ilarl", in !itr = '(), !itr is
the lvalue and '() is the rvalue.
(t is illegal to put an rvalue on the left$hand side of an assign'ent state'ent. &or e#a'ple, '() = BC is il$
legal ecause '() is an rvalue, and Det+nteger() = A is illegal ecause the return value of
Det+nteger() is an rvalue. However, it is legal to put an lvalue on the right$hand side of an assign'ent,
as in A = y or A = !itr.
Chapter 10: Operator Overloading $ 56C $
)t this point the distinction etween lvalues and rvalues see's purel" acade'ic. 2ka", "ou 'ight sa",
so'e things can e put on the left$hand side of an assign'ent state'ent and so'e things can%t. *o
what0 /hen writing overloaded operators, the lvalueDrvalue distinction is e#tre'el" i'portant. -ecause
operator overloading lets us define what the uilt$in operators 'ean when applied to o.ects of class t"pe,
we have to e ver" careful that overloaded operators return lvalues and rvalues appropriatel". &or e#$
a'ple, " default the operator returns an rvalue9 that is, it 'akes no sense to write
(A y) = ,;
*ince this would assign the value C to the result of adding A and y. However, if we%re not careful when
overloading the operator, we 'ight accidentall" 'ake the aove state'ent legal and pave the wa" for
nonsensical ut legal code. *i'ilarl", it is legal to write
my4rrayE,F = '();
*o the ele'ent$selection operator 3the rackets operator4 should e sure to return an lvalue instead of an
rvalue. &ailure to do so will 'ake the aove code illegal when applied to custo' classes.
Becall that an overloaded operator is a speciall"$na'ed function invoked when the operator is applied to
an o.ect of a custo' class t"pe. +hus the code
(A y) = ,;
is e!uivalent to
o&erator (A2 y) = ,;
if either A or y is an o.ect. *i'ilarl", if my4rray is an o.ect, the code
my4rrayE,F = '();
is e!uivalent to
my4rray.o&eratorEF(,) = '();
+o ensure that these functions have the correct se'antics, we need to 'ake sure that o&erator returns
an rvalue and that o&eratorEF returns an lvalue. How can we enforce this restriction0 +he answer has to
do with the return t"pe of the two functions. +o 'ake a function that returns an lvalue, have that function
return a non$const reference. &or e#a'ple, the following function returns an lvalue:
string9 GVa$ueHunction();
-ecause a reference is .ust another na'e for a variale or 'e'or" location, this function hands ack a ref$
erence to an lvalue and its return value can e treated as such. +o have a function return an rvalue, have
that function return a const o.ect " value. +hus the function
const string 7Va$ueHunction();
$ 56E $ Chapter 10: Operator Overloading
returns an rvalue.
F
+he reason that this trick works is that if we have a function that returns a const o$
.ect, then code like
7Va$ueHunction() = '();
is illegal ecause the return value of 7Va$ueHunction is 'arked const.
,values and rvalues are difficult to understand in the astract, ut as we egin to actuall" overload partic$
ular operators the difference should eco'e clearer.
Overloading the Element Seletion Operator
,et%s egin our descent into the real' of operator overloading " discussing the overloaded ele'ent selec$
tion operator 3the E F operator, used to select ele'ents fro' arra"s4. :ou%ve een using the overloaded
ele'ent selection operator ever since "ou encountered the string and vector classes. &or e#a'ple, the
following code uses the vector%s overloaded ele'ent selection operator:
for(int i = I; i < myVector.si>e(); i)
myVectorEiF = '();
(n the aove e#a'ple, while it looks like we%re treating the vector as a pri'itive arra", we are instead call$
ing the a function na'ed o&erator EF, passing i as a para'eter. +hus the aove code is e!uivalent to
for(int i = I; i < myVector.si>e(); i)
myVector.o&erator EF(i) = '();
+o write a custo' ele'ent selection operator, "ou write a 'e'er function called o&erator EF that ac$
cepts as its para'eter the value that goes inside the rackets. Note that while this para'eter can e of an"
t"pe 3think of the *+, ma&4, "ou can onl" have a single value inside the rackets. +his 'a" see' like an ar$
itrar" restriction, ut 'akes sense in the conte#t of the principle of least astonish'ent: "ou can%t put
'ultiple values inside the rackets when working with raw C++ arra"s, so "ou shouldn%t do so when work$
ing with custo' o.ects.
/hen writing o&erator EF, as with all overloaded operators, "ou%re free to return o.ects of whatever
t"pe "ou%d like. However, re'e'er that when overloading operators, it%s essential to 'aintain the sa'e
functionalit" "ou%d e#pect fro' the naturall"$occurring uses of the operator. (n the case of the ele'ent se$
lection operator, this 'eans that the return value should e an lvalue, and in particular a reference to so'e
internal class data deter'ined " the inde#. &or e#a'ple, here%s one possile protot"pe of the C++
string%s ele'ent selection operator:
c$ass string 8
&ub$ic:
1! ... !1

c.ar9 o&erator EF (si>e@t &osition);
:;
F +echnicall" speaking an" non$reference value returned fro' a function is an rvalue. However, when returning o$
.ects fro' a function, the rvalueDlvalue distinction is lurred ecause the assign'ent operator and other operat$
ors are 'e'er functions that can e invoked regardless of whether the receiver is an rvalue or lvalue. +he addi $
tional const closes this loophole.
Chapter 10: Operator Overloading $ 56G $
Here, o&eratorEF takes in an int representing the inde# and returns a reference to the character at that
position in the string. (f string is i'ple'ented as a wrapper for a raw C string, then one possile i'$
ple'entation for o&eratorEF 'ight e
c.ar9 string::o&eratorEF (si>e@t indeA) 8
return t.eStringEindeAF; 11 4ssuming t.eString is an array of c.aracters
:
-ecause o&eratorEF returns a reference to an ele'ent, it is co''on to find o&eratorEF paired with a
const$overload that returns a const reference to an ele'ent in the case where the receiver o.ect is i'$
'utale. +here are e#ceptions to this rule, such as the *+, ma&, ut in the case of string we should
provide a const overload, as shown here:
c$ass string 8
&ub$ic:
1! ... !1
c.ar9 o&erator EF (si>e@t &osition);
const c.ar9 o&erator EF (si>e@t &osition) const;
:;
+he i'ple'entation of the const o&eratorEF function is identical to the non$const version.
/hen writing the ele'ent selection operator, it%s co'pletel" legal to 'odif" the receiver o.ect in re$
sponse to a re!uest. &or e#a'ple, with the *+, ma&, o&eratorEF will silentl" create a new o.ect and re$
turn a reference to it if the ke" isn%t alread" in the ma&. +his is part of the eaut" of overloaded operators 8
"ou%re allowed to perfor' an" necessar" steps to ensure that the operator 'akes sense.
<nfortunatel", if "our class encapsulates a 'ultidi'ensional o.ect, such as a 'atri# or hierarchical ke"$
value s"ste', "ou cannot overload the EFEF operator. ) class is onl" allowed to overload one level of the
racket s"nta#9 it%s not legal to design o.ects that doul"$overload EF.
F
Overloading Compound Assignment Operators
+he co'pound assign'ent operators are operators of the for' op= 3for e#a'ple, = and !=4 that update
an o.ect%s value ut do not overwrite it. Co'pound assign'ent operators are often declared as 'e'er
functions with the following asic protot"pe:
%y/$ass9 o&erator = (const 0arameter?y&e9 &aram)
&or e#a'ple, suppose we have the following class, which represents a vector in three$di'ensional space:
c$ass Vector(- 8
&ub$ic:
1! ... !1
&rivate:
static const int NJ%@/KK7-+N4?LS = (;
doub$e coordinatesENJ%@/KK7-+N4?LSF;
:;
F +here is a techni!ue called proxy objects that can 'ake code along the lines of myKbMectEAFEyF legal. +he trick is
to define an o&eratorEF function for the class that returns another o.ect that itself overloads o&eratorEF. /e%ll
see this trick used in the upco'ing chapter on a custo' grid class.
$ 56H $ Chapter 10: Operator Overloading
(t is legal to add two 'athe'atical vectors to one another9 the result is the vector whose co'ponents are
the pairwise su' of each of the co'ponents of the source vectors. (f we wanted to define a = operator for
Vector(- to let us perfor' this addition, we would 'odif" the interface of Vector(- as follows:
c$ass Vector(- 8
&ub$ic:
1! ... !1
Vector(-9 o&erator= (const Vector(-9 ot.er);
&rivate:
static const int NJ%@/KK7-+N4?LS = (;
doub$e coordinatesENJ%@/KK7-+N4?LSF;
:;
+his could then e i'ple'ented as
Vector(-9 Vector(-::o&erator =(const Vector(-9 ot.er) 8
for(int i = I; i < NJ%@/KK7-+N4?LS; i)
coordinatesEiF = ot.er.coordinatesEiF;
return !t.is;
:
(f "ou%ll notice, o&erator= returns !t.is, a reference to the receiver o.ect. Becall that when overload$
ing operators, "ou should 'ake sure to define "our operators such that the" work identicall" to the C++
uilt$in operators. (t turns out that the = operator "ields an lvalue, so the code elow, though the !uint$
essence of a"s'al st"le, is perfectl" legal:
int one2 t#o2 t.ree2 four;
(one = t#o) = (t.ree = four);
*ince overloaded operators let custo' t"pes act like pri'itives, the following code should also co'pile:
Vector(- one2 t#o2 t.ree2 four;
(one = t#o) = (t.ree = four);
(f we e#pand out the calls to the overloaded = operator, we find that this is e!uivalent to
Vector(- one2 t#o2 t.ree2 four;
one.o&erator=(t#o).o&erator =(t.ree.o&erator =(four));
Note that the reference returned " one.o&erator=(t#o) then has its own = operator invoked. *ince
o&erator = is not 'arked const, had the = operator returned a const reference, this code would have
een illegal. Aake sure to have an" 3co'pound4 assign'ent operator return !t.is as a non$const refer$
ence.
<nlike the regular assign'ent operator, with the co'pound assign'ent operator it%s co''onl" 'eaning$
ful to accept o.ects of different t"pes as para'eters. &or e#a'ple, we 'ight want to 'ake e#pressions
like myVector != '() for Vector(-s 'eaningful as a scaling operation. (n this case, we can si'pl"
define an o&erator != that accepts a doub$e as its para'eter. &or e#a'ple:
Chapter 10: Operator Overloading $ 566 $
c$ass Vector(- 8
&ub$ic:
1! ... !1
Vector(-9 o&erator = (const Vector(-9 ot.er);
Vector(-9 o&erator != (doub$e sca$eHactor);
&rivate:
static const int NJ%@/KK7-+N4?LS = (;
doub$e coordinatesENJ%@/KK7-+N4?LSF;
:;
1espite the fact that the receiver and para'eter have different t"pes, this is perfectl" legal C++. Here%s one
possile i'ple'entation:
Vector(-9 Vector(-::o&erator!= (doub$e sca$eHactor) 8
for(int k = I; k < NJ%@/KK7-+N4?LS; k)
coordinatesEkF != sca$eHactor;
return !t.is;
:
Although we have implemented o&erator= and o&erator!= for the Vector(- class, C++ will not automat-
ically provide us an implementation of o&erator*= and o&erator1=, despite the fact that those functions can
easily be implemented as wrapped calls to the operators we've already implemented. This might seem counterin-
tuitive, but prevents errors from cases where seemingly symmetric operations are undefined. For eample, it is
legal to multiply a vector and a matri, though the division is undefined. For completeness' sa!e, we'll prototype
o&erator*= and o&erator1= as shown here"
c$ass Vector(- 8
&ub$ic:
1! ... !1
Vector(-9 o&erator = (const Vector(-9 ot.er);
Vector(-9 o&erator *= (const Vector(-9 ot.er);
Vector(-9 o&erator != (doub$e sca$eHactor);
Vector(-9 o&erator 1= (doub$e sca$eHactor);
&rivate:
static const int NJ%@/KK7-+N4?LS = (;
doub$e coordinatesENJ%@/KK7-+N4?LSF;
:;
#ow, how might we go about implementing these operators$ o&erator1= is the simplest of the two and can be
implemented as follows"
Vector(-9 Vector(-::o&erator 1= (doub$e sca$eHactor) 8
!t.is != '.I 1 sca$eHactor;
return !t.is;
:
This implementation, though cryptic, is actually %uite elegant. The first line, !t.is != '.I 1 sca$eHact*
or, says that we should multiply the receiver ob&ect '!t.is( by the reciprocal of sca$eHactor. The != oper-
ator is the compound multiplication assignment operator that we wrote above, so this code invo!es o&erator!=
on the receiver. )n fact, this code is e%uivalent to
$ ?77 $ Chapter 10: Operator Overloading
Vector(-9 Vector(-::o&erator 1= (doub$e sca$eHactor) 8
o&erator!= ('.I 1 sca$eHactor);
return !t.is;
*
+epending on your taste, you might find this particular synta more readable than the first version. Feel free to
use either version.
#ow, how would we implement o&erator*=, which performs a componentwise subtraction of two Vec*
tor(-s$ At a high level, subtracting one vector from another is e%ual to adding the inverse of the second vector
to the first, so we might want to write code li!e this"
Vector(-9 Vector(-::o&erator *= (const Vector(-9 ot.er) 8
!t.is = *ot.er;
return !t.is;
:
That is, we add *ot.er to the receiver ob&ect. ,ut this code is illegal because we haven't defined the unary
minus operator as applied to Vector(-s. #ot to worry - we can overload this operator as well. The synta for
this function is as follows"
c$ass Vector(- 8
&ub$ic:
1! ... !1
Vector(-9 o&erator = (const Vector(-9 ot.er);
Vector(-9 o&erator *= (const Vector(-9 ot.er);
Vector(-9 o&erator != (doub$e sca$eHactor);
Vector(-9 o&erator 1= (doub$e sca$eHactor);
const Vector(- o&erator* () const;
&rivate:
static const int NJ%@/KK7-+N4?LS = (;
doub$e coordinatesENJ%@/KK7-+N4?LSF;
*.
There are four pieces of information about this function that deserve attention"
The name of the unary minus function is o&erator *.
The function ta!es no parameters. This lets C++ !now that the function is the unary minus function ').e.
*myVector( rather than the binary minus function 'myVector N myKt.erVector(.
The function returns a const Vector(-. The unary minus function returns an rvalue rather than an
lvalue, since code li!e *A = '() is illegal. As mentioned above, this means that the return value of this
function should be a const Vector(-.
The function is mar!ed const. Applying the unary minus to an ob&ect doesn't change its value, and to
enforce this restriction we'll mar! o&erator N const.
/ne possible implementation of o&erator* is as follows"
Chapter 10: Operator Overloading $ ?7; $
const Vector(- Vector(-::o&erator* () const 8
Vector(- resu$t;
for(int k = I; k < NJ%@/KK7-+N4?LS; k)
resu$t.coordinatesEkF = *coordinatesEkF;
return resu$t;
:
#ote that the return type of this function is const Vector(- while the type of resu$t inside the function is
Vector(-. This isn't a problem, since returning an ob&ect from a function yields a new temporary ob&ect and it's
legal to initiali0e a const Vector(- using a Vector(-.
1hen writing compound assignment operators, as when writing regular assignment operators, you must be care-
ful that self-assignment wor!s correctly. )n the above eample with Vector(-'s compound assignment operat-
ors we didn't need to worry about this because the code was structured correctly. 2owever, when wor!ing with
the C++ string's = operator, since the string needs to allocate a new buffer capable of holding the current
string appended to itself, it would need to handle the self-assignment case, either by eplicitly chec!ing for
self-assignment or through some other means.
Overloading !athematial Operators
(n the previous section, we provided overloaded versions of the = fa'il" of operators. +hus, we can now
write classes for which e#pressions of the for' one = t#o are valid. However, the see'ingl" e!uivalent
e#pression one = one t#o will still not co'pile, since we haven%t provided an i'ple'entation of the
lone operator. C++ will not auto'aticall" provide i'ple'entations of related operators given a single
overloaded operator, since in so'e cases this could result in nonsensical or 'eaningless ehavior.
+he uilt$in 'athe'atical operators "ield rvalues, so code like (A y) = '() will not co'pile. Con$
se!uentl", when overloading the 'athe'atical operators, 'ake sure the" return rvalues as well " having
the' return const o.ects.
,et%s consider an i'ple'entation of o&erator for our Vector(- class. -ecause the operator "ields an
rvalue, we%re supposed to return a const Vector(-, and ased on our knowledge of para'eter passing,
we know that we should accept a const Vector(- 9 as a para'eter. +here%s one 'ore it we%re forget$
ting, though, and that%s to 'ark the o&erator function const, since o&erator creates a new o.ect
and doesn%t 'odif" either of the values used in the arith'etic state'ent. +his results in the following
code
c$ass Vector(- 8
&ub$ic:
1! ... !1
Vector(-9 o&erator = (const Vector(-9 ot.er);
const Vector(- o&erator (const Vector(-9 ot.er);
Vector(-9 o&erator *= (const Vector(-9 ot.er);
Vector(-9 o&erator != (doub$e sca$eHactor);
Vector(-9 o&erator 1= (doub$e sca$eHactor);
const Vector(- o&erator* () const;
&rivate:
static const int NJ%@/KK7-+N4?LS = (;
doub$e coordinatesENJ%@/KK7-+N4?LSF;
*.
$ ?75 $ Chapter 10: Operator Overloading
How are we to i'ple'ent this function0 /e could .ust do a co'ponent$"$co'ponent addition, ut it%s
actuall" 'uch easier to .ust write the function in ter's of our o&erator =. +he full version of this code
is shown elow:
const Vector(- Vector(-::o&erator (const Vector(-9 ot.er) const 8
Vector(d resu$t = !t.is; 11 %ake a dee& co&y of t.is Vector(-.
resu$t = ot.er; 11 Jse eAisting addition code.
return resu$t;
:
Now, all of the code for o&erator is unified, which helps cut down on coding errors.
+here is an interesting and co''on case we haven%t addressed "et 8 what if one of the operands isn%t of
the sa'e t"pe as the class0 &or e#a'ple, if "ou have a %atriA class that encapsulates a ?#? 'atri#, as
shown here:
c$ass %atriA 8
&ub$ic:
1! ... !1
%atriA9 o&erator != (doub$e sca$ar); 11 Sca$e a$$ entries
&rivate:
static const int %4?7+O@S+PL = (;
doub$e entriesE%4?7+O@S+PLFE%4?7+O@S+PLF;
:;
Note that there is a defined != operator that scales all ele'ents in the 'atri# " a doub$e factor. +hus
code like my%atriA != C.)'QCQ is well$defined. However, since there%s no defined o&erator !, cur$
rentl" we cannot write my%atriA = my%atriA ! C.)'QCQ.
(nitiall", "ou 'ight think that we could define o&erator ! .ust as we did o&erator in the previous e#$
a'ple. /hile this will work in 'ost cases, it will lead to so'e prole's we%ll need to address later. &or
now, however, let%s add the 'e'er function o&erator ! to %atriA, which is defined as
const %atriA %atriA::o&erator !(doub$e sca$ar) const 8
%y%atriA resu$t = !t.is;
resu$t != sca$ar;
return resu$t;
:
Now, we can write e#pressions like my%atriA = my%atriA ! C.)'QCQ. However, what happens if we
write code like my%atriA = C.)'QCQ ! my%atriA0 +his is a se'anticall" 'eaningful e#pression, ut
unfortunatel" it won%t co'pile. /hen interpreting overloaded operators, C++ will alwa"s preserve the or$
der of values in an e#pression.
F
+hus C.)'QCQ ! my%atriA is not the sa'e as my%atriA ! C.)'QCQ.
Be'e'er that the reason that my%atriA ! C.)'QCQ is legal is ecause it%s e!uivalent to my%atriA.o&*
erator !(C.)'QCQ). +he e#pression C.)'QCQ ! my%atriA, on the other hand, is illegal ecause C++
will tr" to e#pand it into (C.)'QCQ).o&erator !(my%atriA), which 'akes no sense.
F 2ne 'a.or reason for this is that so'eti'es the arith'etic operators won%t e co''utative. &or e#a'ple, given
'atrices A and ", A" is not necessaril" the sa'e as "A, and if C++ were to aritraril" flip para'eters it could res$
ult in so'e e#tre'el" difficult$to$track ugs.
Chapter 10: Operator Overloading $ ?7? $
+o fi# this, we can 'ake o&erator ! a free function that accepts two para'eters, a doub$e and a %atriA,
and returns a const %atriA. +hus code like C.)'QCQ ! my%atriA will e#pand into calls to o&erat*
or !(C.)'QCQ2 my%atriA). +he new version of o&erator ! is defined elow:
const %atriA o&erator ! (doub$e sca$ar2 const %atriA9 matriA) 8
%atriA resu$t = !matriA;
matriA != sca$ar;
return resu$t;
:
-ut here we run into the sa'e prole' as efore if we write my%atriA ! C.)'QCQ, since we haven%t
defined a function accepting a %atriA as its first para'eter and an doub$e as its second. +o fi# this, we%ll
define a second free function o&erator ! with the para'eters reversed that%s i'ple'ented as a call to
the other version:
const %atriA o&erator !(const %atriA9 matriA2 doub$e sca$ar) 8
return sca$ar ! matriA; 11 /a$$s o&erator! (sca$ar2 matriA)
:
)s a general rule, 'athe'atical operators like should alwa"s e i'ple'ented as free functions. +his
prevents prole's like those descried here.
2ne i'portant point to notice aout overloading the 'athe'atical operators versus the co'pound assign$
'ent operators is that it%s consideral" faster to use the co'pound assign'ent operators over the stan$
dalone 'athe'atical operators. Not onl" do the co'pound assign'ent operators work in$place 3that is,
the" 'odif" e#isting o.ects4, ut the" also return references instead of full o.ects. &ro' a perfor'ance
standpoint, this 'eans that given these three strings:
string one = "?.is ";
string t#o = "is a ";
string t.ree = "string!";
Consider these two code snippets to concatenate all three strings:
/* Using += */
string myString = one;
myString = t#o;
myString = t.ree;
/* Using + */
string myString = one t#o t.ree
2ddl", the second version of this code is consideral" slower than the first ecause the operator gener$
ates te'porar" o.ects. Be'e'er that one t#o t.ree is e!uivalent to
o&erator (one2 o&erator (t#o2 t.ree))
>ach call to o&erator returns a new string for'ed " concatenating the para'eters, so the code one
t#o t.ree creates two te'porar" string o.ects. +he first version, on the other hand, generates
no te'porar" o.ects since the = operator works in$place. +hus while the first version is less sightl", it is
significantl" faster than the second.
$ ?7@ $ Chapter 10: Operator Overloading
Overloading ++ and --
2verloading the incre'ent and decre'ent operators can e a it trick" ecause there are two versions of
each operator: prefix and postfix. Becall that A and A are different operations 8 the first will evaluate
to the value of A, then incre'ent A, while the second will incre'ent A and then evaluate to the updated
value of A. :ou can see this elow:
int A = I
cout << A << end$; 11 0rints: I
cout << A << end$; 11 0rints: '
A = I;
cout << A << end$; 11 0rints: '
cout << A << end$; 11 0rints: '
)lthough this distinction is sutle, it%s tre'endousl" i'portant for efficienc" reasons. (n the postfi# ver$
sion of , since we have to return the value of the variale was efore it was incre'ented, we%ll need to
'ake a full cop" of the old version and then return it. /ith the prefi# , since we%re returning the current
value of the variale, we can si'pl" return a reference to it. +hus the postfi# can e noticeal" slower
than the prefi# version9 this is the reason that when advancing an *+, iterator it%s faster to use the prefi#
incre'ent operator.
+he ne#t !uestion we need to address is how we can legall" use and ** in regular code. <nfortunatel",
it can get a it co'plicated. &or e#a'ple, the following code is totall" legal:
int A = I;
A; 11 +ncrements A seven times.
+his is legal ecause it%s e!uivalent to
((((((A))))));
+he prefi# operator returns the variale eing incre'ented as an lvalue, so this state'ent 'eans in$
cre'ent #, then incre'ent # again, etc.
However, if we use the postfi# version of , as seen here:
A; 11 Lrror
/e get a co'pile$ti'e error ecause A returns the original value of # as an rvalue, which can%t e incre$
'ented ecause that would re!uire putting the rvalue on the left side of an assign'ent 3in particular,
# I # + ;4.
Now, let%s actuall" get into so'e code. <nfortunatel", we can%t .ust sit down and write o&erator , since
it%s unclear which o&erator we%d e overloading. C++ uses a hack to differentiate etween the prefi#
and postfi# versions of the incre'ent operator: when overloading the prefi# version of or **, "ou write
o&erator as a function that takes no para'eters. +o overload the postfi# version, "ou%ll overload o&*
erator , ut the overloaded operator will accept as a para'eter the integer value 7. (n code, these two
declarations look like
Chapter 10: Operator Overloading $ ?7C $
c$ass %y/$ass 8
&ub$ic:
1! ... !1
%y/$ass9 o&erator (); 11 0refiA
const %y/$ass o&erator (int dummy); 11 0ostfiA
&rivate:
1! ... !1
:;
Note that the prefi# version returns a %y/$ass9 as an lvalue and the postfi# version a const %y/$ass as
an rvalue.
/e%re allowed to i'ple'ent and ** in an" wa" we see fit. However, one of the 'ore co''on tricks is
to write the i'ple'entation as a wrapped call to o&erator =. )ssu'ing "ou%ve provided this func$
tion, we can then write the prefi# o&erator as
%y/$ass9 %y/$ass::o&erator () 8
!t.is = ';
return !t.is;
:
)nd the postfi# o&erator as
const %y/$ass %y/$ass::o&erator (int dummy) 8
%y/$ass o$dVa$ue = !t.is; 11 Store t.e current va$ue of t.e obMect.
!t.is;
return o$dVa$ue;
:
Notice that the postfi# o&erator is i'ple'ented in ter's of the prefi# o&erator. +his is a fairl"
standard techni!ue and cuts down on the a'ount of code that "ou will need to write for the functions.
(n "our future C++ career, "ou 'a" encounter versions of o&erator that look like this:
const %y/$ass %y/$ass::o&erator (int) 8
%y/$ass o$dVa$ue = !t.is; 11 Store t.e current va$ue of t.e obMect.
!t.is;
return o$dVa$ue;
:
)lthough this function takes in an int para'eter, that para'eter does not have a na'e. (t turns out that it
is perfectl" legal C++ code to write functions that accept para'eters ut do not give na'es to those para$
'eters. (n this case, the function acts .ust like a regular function that accepts a para'eter, e#cept that the
para'eter cannot e used inside of the function. (n the case of o&erator, this helps give a cue to read$
ers that the integer para'eter is not 'eaningful and e#ists solel" to differentiate the prefi# and postfi#
versions of the function.
Overloading Relational Operators
=erhaps the 'ost co''onl" overloaded operators 3other than o&erator =4 are the relational operators9
for e#a'ple, < and ==. <nlike the assign'ent operator, " default C++ does not provide relational operat$
ors for "our o.ects. +his 'eans that "ou 'ust e#plicitl" overload the == and related operators to use
$ ?7E $ Chapter 10: Operator Overloading
the' in code. +he protot"pe for the relational operators looks like this 3written for <, ut can e for an" of
the relational operators4:
c$ass %y/$ass 8
&ub$ic:
1! ... !1
boo$ o&erator < (const %y/$ass9 ot.er) const;
&rivate:
1! ... !1
:;
:ou%re free to choose an" 'eans for defining what it 'eans for one o.ect to e less than another. How$
ever, when doing so, "ou 'ust take great care to ensure that "our less$than operator defines a total order-
ing on o.ects of "our t"pe. +his 'eans that the following 'ust e true aout the ehavior of the less$than
operator:
#rihotom$: &or an" a and b, e#actl" one of a J b, a I b, and b J a is true.
#ransitivit$: (f a J b and b J c, then a J c.
+hese properties of J are i'portant ecause the" allow the notion of sorted order to 'ake sense. (f either
of these conditions does not hold, then it is possile to encounter strange situations in which a collection
of ele'ents cannot e put into ascending order. &or e#a'ple, suppose that we have the following class,
which represents a point in two$di'ensional space:
c$ass 0oint 8
&ub$ic:
0oint(doub$e A2 doub$e y);
doub$e getO() const;
void setO(doub$e va$ue);
doub$e getR() const;
void setR(doub$e va$ue);
:
Now consider the following i'ple'entation of a less$than operator for co'paring 0oints:
boo$ o&erator< (const 0oint9 one2 const 0oint9 t#o) 8
return one.getO() < t#o.getO() 99 one.getR() < t#o.getR();
:
(ntuitivel", this 'a" see' like a reasonale definition of the J operator: point a is less than point b if oth
coordinates of a are less than the corresponding coordinates of b. However, this i'ple'entation of J is
ound to cause prole's. (n particular, consider the following code:
0oint one('2 I)2 t#o(I2 ');
cout << (one < t#o) << end$;
cout << (t#o < one) << end$;
Here, we create two points called one and t#o and co'pare the' using the J operator. /hat will the first
line print0 <sing the aove definition of o&erator<, the co'parison one < t#o will evaluate to false e$
cause the x coordinate of one is greater than the x coordinate of t#o. -ut what aout t#o < one0 (n this
case, t#o%s x coordinate is less than one%s, ut its y coordinate is greater than one%s. Conse!uentl", we
have that t#o < one also evaluates to false. /e have reached a precarious situation. /e have found two
Chapter 10: Operator Overloading $ ?7G $
values, one and t#o, such that one and t#o do not have e!ual values, ut neither is less than the other.
+his 'eans that we could not possil" sort a list of ele'ents containing oth one and t#o, since neither
one precedes the other.
+he prole' with the aove i'ple'entation of o&erator< is that it violates trichoto'". Becall fro' the
aove definition of a total ordering that trichoto'" 'eans that e#actl" one of a J b, a I b, a K b 'ust hold
for an" a and b. 2ur definition of o&erator< does not have this propert", as illustrated aove. Con$
se!uentl", we have a legal i'ple'entation of o&erator< that is wholl" incorrect. /e%ll need to redefine
how o&erator< works in order to ensure that trichoto'" holds.
2ne co''on strateg" for i'ple'enting o&erator< is to use what%s called a lexicographical ordering. +o
illustrate a le#icographicall" ordering, consider the words a%out and a%ove and think aout how "ou
would co'pare the' alphaeticall". :ou%d egin " noting that the first letter of each word was the sa'e,
as was the second and the third. However, the fourth letter of the words disagree, and in particular the let$
ter u fro' a%out precedes the letter v fro' a%ove. Conse!uentl", we would sa" that a%out co'es le#ico$
graphicall" efore a%ove. (nterestingl", though, the last letter of a%out 3t4 co'es after the last letter of
a%ove 3e4. /e don%t care, though, ecause we stopped co'paring letters as soon as we found the first 'is$
'atch in the words.
+his strateg" has an elegant analog for aritrar" t"pes. Liven a t"pe, one wa" to i'ple'ent o&erator< is
as follows. Liven two o.ects a and b of that t"pe, check whether the first field of a and b are not the sa'e.
(f so, sa" that a is s'aller if its first field is s'aller than b%s first field. 2therwise, look at the second field.
(f the fields are not the sa'e, then return a if a%s second field is s'aller than b%s and b otherwise. (f not,
then look at the third field, etc. +o give "ou a concrete e#a'ple of how this works, consider the following
revision to the 0oint%s o&erator< function:
boo$ o&erator< (const 0oint9 one2 const 0oint9 t#o) 8
if (one.getO() != t#o.getO()) return one.getO() < t#o.getO();
return one.getR() < t#o.getR();
:
Here, we first check whether the points disagree in their x coordinate. (f so, we sa" that one is less than
t#o onl" if it has a s'aller x coordinate. 2therwise, if the points agree in their x coordinate, then
whichever has the lower y coordinate is said to have the s'aller value. )'azingl", this i'ple'entation
strateg" results in an ordering that is oth trichotic and transitive, e#actl" the properties we want out of
the J operator.
2f course, this strateg" works on classes that have 'ore than two fields, provided that "ou co'pare each
field one at a ti'e. (t is an interesting e#ercise to convince "ourself that a le#icographical ordering on an"
t"pe oe"s trichoto'", and that such an ordering oe"s transitivit" as well.
2nce "ou have a working i'ple'entation of o&erator<, it is possile to define all five other relational op$
erators solel" in ter's of the o&erator<. +his is due to the following set of relations:
4 < S 4 < S
4 <= S !(S < 4)
4 == S !(4 < S TT S < 4)
4 != S 4 < S TT S < 4
4 >= S !(4 < S)
4 > S S < 4
$ ?7H $ Chapter 10: Operator Overloading
&or e#a'ple, we could i'ple'ent o&erator> for the 0oint class as
boo$ o&erator> (const 0oint9 one2 const 0oint9 t#o) 8
return t#o < one;
:
/e could si'ilarl" i'ple'ent o&erator<= for 0oints as
boo$ o&erator<= (const 0oint9 one2 const 0oint9 t#o) 8
return !(one < t#o);
:
+his is a fairl" standard techni!ue, and it%s well worth the effort to re'e'er it.
Storing O%&ets in S#L maps
<p to this point we%ve avoided storing o.ects as ke"s in *+, ma&s. Now that we%ve covered operator over$
loading, though, "ou have the necessar" knowledge to store o.ects in the *+, ma& and set containers.
(nternall", the *+, ma& and set are la"ered on inar" trees that use the relational operators to co'pare
ele'ents. 1ue to so'e clever design decisions, *+, containers and algorith's onl" re!uire the < operator
to co'pare two o.ects. +hus, to store a custo' class inside a ma& or set, "ou si'pl" need to overload the
< operator and the *+, will handle the rest. &or e#a'ple, here%s so'e code to store a 0oint struct in a
ma&:
struct &oint? 8
int A2 y;
boo$ o&erator < (const &oint?9 ot.er) const 8
if(A != ot.er.A)
return A < ot.er.A;
return y < ot.er.y;
:
:;
ma&<&oint?2 int> my%a&; 11 No# #orks using t.e defau$t < o&erator.
:ou can use a si'ilar trick to store o.ects as values in a set.
friend
Nor'all", when "ou 'ark a class%s data 'e'ers private, onl" instances of that class are allowed to access
the'. However, in so'e cases "ou 'ight want to allow specific other classes or functions to 'odif"
private data. &or e#a'ple, if "ou were i'ple'enting the *+, ma& and wanted to provide an iterator class
to traverse it, "ou%d want that iterator to have access to the ma&%s underl"ing inar" tree. +here%s a slight
prole' here, though. )lthough the iterator is an integral co'ponent of the ma&, like all other classes, the
iterator cannot access private data and thus cannot traverse the tree.
How are we to resolve this prole'0 :our initial thought 'ight e to 'ake so'e pulic accessor 'ethods
that would let the iterator 'odif" the o.ect%s internal data representation. <nfortunatel", this won%t work
particularl" well, since then any class would e allowed to use those functions, so'ething that violates the
principle of encapsulation. (nstead, to solve this prole', we can use the C++ friend ke"word to grant
the iterator class access to the ma& or set%s internals. (nside the ma& declaration, we can write the follow$
ing:
Chapter 10: Operator Overloading $ ?76 $
tem&$ate <ty&ename Uey?y&e2 ty&ename Va$ue?y&e> c$ass ma& 8
&ub$ic:
1! ... !1
friend c$ass iterator;
c$ass iterator 8
1! ... iterator im&$ementation .ere ... !1
:;
:;
Now, since iterator is a friend of ma&, it can read and 'odif" the ma&%s private data 'e'ers.
Must as we can grant other classes friend access to a class, we can give friend access to gloal functions.
&or e#a'ple, if we had a free function %odify%y/$ass that accepted a %y/$ass o.ect as a reference
para'eter, we could let %odify%y/$ass 'odif" the internal data of %y/$ass if inside the %y/$ass declar$
ation we added the line
c$ass %y/$ass 8
&ub$ic:
1! ... !1
friend void %odify%y/$ass(%y/$ass9 &aram);
:;
+he s"nta# for friend can e 'isleading. >ven though we%re protot"ping %odify%y/$ass inside the %y*
/$ass function, ecause %odify%y/$ass is a friend of %y/$ass it is not a 'e'er function of %y/$ass.
)fter all, the purpose of the friend declaration is to give outside classes and functions access to the %y/*
$ass internals.
/hen using friend, there are two ke" points to e aware of. &irst, the friend declaration 'ust precede
the actual i'ple'entation of the friend class or function. *ince C++ co'pilers onl" 'ake a single pass
over the source file, if the" haven%t seen a friend declaration for a function or class, when the function or
class tries to 'odif" "our o.ect%s internals, the co'piler will generate an error. *econd, note that while
friend is !uite useful in so'e circu'stances, it can !uickl" lead to code that entirel" defeats the purpose
of encapsulation. -efore "ou grant friend access to a piece of code, 'ake sure that the code has a legiti'$
ate reason to e 'odif"ing "our o.ect. +hat is, don%t 'ake code a friend si'pl" ecause it%s easier to
write that wa". +hink of friend as a wa" of e#tending a class definition to include other pieces of code.
+he class, together with all its friend code, should co'prise a logical unit of encapsulation.
/hen overloading an operator as a free function, "ou 'ight want to consider giving that function friend
access to "our class. +hat wa", the functions can efficientl" read "our o.ect%s private data without having
to go through getters and setters.
<nfortunatel", friend does not interact particularl" intuitivel" with te'plate classes. *uppose we want to
provide a friend function 0VueueHriend for a te'plate version of the C*;7E-DN 0Vueue. (f
0VueueHriend is declared like this:
tem&$ate <ty&ename ?> void 0VueueHriend(const 0Vueue<?>9 &W) 8
1! ... !1
:
:ou%ll notice that 0VueueHriend itself is a te'plate function. +his 'eans that when declaring
0VueueHriend a friend of the te'plate 0Vueue, we%ll need to 'ake the friend declaration te'platized,
as shown here:
$ ?;7 $ Chapter 10: Operator Overloading
tem&$ate <ty&ename ?> c$ass 0Vueue 8
&ub$ic:
1! ... !1
tem&$ate <ty&ename ?> friend 0VueueHriend(const 0Vueue<?>9 &W);
:;
(f "ou forget the tem&$ate declaration, then "our code will co'pile correctl" ut will generate a linker er$
ror. /hile this can e a it of nuisance, it%s i'portant to re'e'er since it arises fre!uentl" when over$
loading the strea' operators, as "ou%ll see elow.
Overloading the Stream 'nsertion Operator
Have "ou ever wondered wh" cout << "5e$$o2 #or$d!" << end$ is s"ntacticall" legal0 (t%s through
the overloaded << operator in con.unction with ostreams.
F
(n fact, the entire strea's lirar" can e
thought of as a gigantic lirar" of 'assivel" overloaded << and >> operators.
+he C++ strea's lirar" is designed to give "ou 'a#i'u' fle#iilit" with "our input and output routines
and even lets "ou define "our own strea' insertion and e#traction operators. +his 'eans that "ou are al$
lowed to define the << and >> operators so that e#pressions like cout << my/$ass << end$ and cin >>
my/$ass are well$defined. However, when writing strea' insertion and e#traction operators, there are
huge nu'er of considerations to keep in 'ind, 'an" of which are e"ond the scope of this te#t. +his ne#t
section will discuss asic strategies for overloading the << operator, along with so'e li'itations of the
si'ple approach.
)s with all overloaded operators, we need to consider what the para'eters and return t"pe should e for
our overloaded << operator. -efore considering para'eters, let%s think of the return t"pe. /e know that it
should e legal to chain strea' insertions together 8 that is, code like cout << ' << C << end$ should
co'pile correctl". +he << operator associates to the left, so the aove code is e!ual to
(((cout << ') << C) << end$)(
+hus, we need the << operator to return an ostream. Now, we don%t want this strea' to e const, since
then we couldn%t write code like this:
cout << "?.is is a string!" << set#('I) << end$;
*ince if cout << "?.is is a string!" evaluated to a const o.ect, we couldn%t set the width of the
ne#t operation to ;7. )lso, we cannot return the strea' " value, since strea' classes have their cop"
functions 'arked private. =utting these two things together, we see that the strea' operators should re$
turn a non$const reference to whatever strea' the"%re referencing.
Now let%s consider what para'eters we need. /e need to know what strea' we want to write to or read
fro', so initiall" "ou 'ight think that we%d define overloaded strea' operators as 'e'er functions that
look like this:
c$ass %y/$ass 8
&ub$ic:
ostream9 o&erator << (ostream9 in&ut) const; 11 Problem: Gega$ but incorrect
:;
F )s a re'inder, the ostream class is the ase class for output strea's. +his has to do with inheritance, which we%ll
cover in a later chapter, ut for now .ust realize that it 'eans that oth stringstream and ofstream are special$
izations of the 'ore generic ostream class.
Chapter 10: Operator Overloading $ ?;; $
<nfortunatel", this isn%t correct. Consider the following two code snippets:
cout << my/$ass;
my/$ass << cout;
+he first of these two versions 'akes sense, while the second is ackwards. <nfortunatel", with the aove
definition of o&erator <<, we%ve accidentall" 'ade the second version s"ntacticall" legal. +he reason is
that these two lines e#pand into calls to
cout.o&erator <<(my/$ass);
my/$ass.o&erator <<(cout);
+he first of these two isn%t defined, since cout doesn%t have a 'e'er function capale of writing our o$
.ect 3if it did, we wouldn%t need to write a strea' operator in the first place!4. However, ased on our pre$
vious definition, the second version, while se'anticall" incorrect, is s"ntacticall" legal. *o'ehow we need
to change how we define the strea' operator so that we are allowed to write cout << my/$ass. +o fi#
this, we%ll 'ake the overloaded strea' operator a free function that takes two para'eters 8 an ostream to
write to and a my/$ass o.ect to write. +he code for this is:
ostream9 o&erator << (ostream9 stream2 const %y/$ass9 mc) 8
1! ... im&$ementation ... !1
return stream;
:
/hile this code will work correctl", ecause o&erator << is a free function, it doesn%t have access to an"
of the private data 'e'ers of %y/$ass. +his can e a nuisance, since we%d like to directl" write the con$
tents of %y/$ass out to the strea' without having to go through the 3possil" inefficient4 getters and set$
ters. +hus, we%ll declare o&erator << a friend inside the %y/$ass declaration, as shown here:
c$ass %y/$ass 8
&ub$ic:
1! %ore functions. !1
friend ostream9 o&erator <<(ostream9 stream2 const %y/$ass9 mc);
:;
Now, we%re all set to do reading and writing inside the od" of the insertion operator. (t%s not particularl"
difficult to write the strea' insertion operator 8 all that we need to do is print out all of the 'eaningful
class infor'ation with so'e for'atting infor'ation. *o, for e#a'ple, given a 0oint class representing a
point in 5$1 space, we could write the insertion operator as
ostream9 o&erator <<(ostream9 stream2 const 0oint9 &t) 8
stream << X(X << &t.A << "2 " << &t.y << X)X;
return stream;
:
/hile this code will work in 'ost cases, there are a few spots where it .ust won%t work correctl". &or e#$
a'ple, suppose we write the following code:
cout << "I'C(B,Y)QZI'C(B,Y)QZ" << end$; 11 ?o see t.e number of c.aracters.
cout << set#(CI) << my0oint << end$;
,ooking at this code, "ou%d e#pect that it would cause my0oint to e printed out and padded with space
characters until it is at least twent" characters wide. <nfortunatel", this isn%t what happens. *ince o&er*
ator << writes the o.ect one piece at a ti'e, the output will look so'ething like this:
$ ?;5 $ Chapter 10: Operator Overloading
0123456!"0123456!"
#0$ 4%
+hat%s nineteen spaces, followed " the actual 0oint data. +he prole' is that when we invoke o&erator
<<, the function writes a single ( character to stream. (t%s this operation, not the Point as a whole, that
will get aligned to 57 characters. +here are 'an" wa"s to circu'vent this prole', ut perhaps the
si'plest is to uffer the output into a stringstream and then write the contents of the stringstream to
the destination in a single operation. +his can get a it co'plicated, especiall" since "ou%ll need to cop"
the strea' for'atting infor'ation over.
/riting a correct strea' e#traction operator 3o&erator >>4 is co'plicated. &or 'ore infor'ation on
writing strea' e#traction operators, consult a reference.
Overloading * and -&
Consider the following code snippet:
for(set<string>::iterator itr = mySet.begin(); itr != mySet.end(); itr)
cout << !itr << " .as $engt. " << itr*>$engt.() << end$;
Here, we traverse a set<string> using iterators, printing out each string and its length. (nterestingl",
even though set iterators are not raw pointers 3the"%re o.ects capale of traversing inar" trees4, thanks
to operator overloading, the" can respond to the ! and *> operators as though the" were regular C++
pointers.
(f "ou create a custo' class that acts like a C++ pointer 3perhaps a custo' iterator or s'art pointer, a
topic we%ll return to later4, "ou can provide i'ple'entations of the pointer dereference and 'e'er se$
lection operators ! and *> " overloading their respective operator functions. +he si'pler of these two
functions is the pointer dereference operator. +o 'ake an o.ect that can e dereferenced to "ield an o$
.ect of t"pe ?, the s"nta# for its ! operator is
c$ass 0ointer/$ass 8
&ub$ic:
?9 o&erator !() const;
1! ... !1
:;
:ou can invoke the o&erator ! function " dereferencing the custo' pointer o.ect. &or e#a'ple, the
following code:
!my/ustom0ointer = '();
is co'pletel" e!uivalent to
my/ustom0ointer.o&erator !() = '();
-ecause we can assign a value to the result of o&erator !, the o&erator ! function should return an
lvalue 3a non$const reference4.
+here are two other points worth noting here. &irst, how can C++ distinguish this o&erator ! for pointer
dereference fro' the o&erator ! used for 'ultiplication0 +he answer has to do with the nu'er of
para'eters to the function. *ince a pointer dereference is a unar" operator, the function protot"pe for the
pointer$dereferencing o&erator ! takes no para'eters. Had we wanted to write o&erator ! for 'ulti$
Chapter 10: Operator Overloading $ ?;? $
plication, we would have written a function o&erator ! that accepts a para'eter 3or a free function ac$
cepting two para'eters4. *econd, wh" is o&erator ! 'arked const0 +his has to do with the difference
etween const pointers and pointers$to$const. *uppose that we have a const instance of a custo'
pointer class. *ince the pointer object is const, it acts as though it is a const pointer rather than a point$
er$to$const. Conse!uentl", we should e ale to dereference the o.ect and 'odif" its stored pointer
without affecting its constness.
+he arrow operator o&erator *> is slightl" 'ore co'plicated than o&erator !. (nitiall", "ou 'ight
think that o&erator *> would e a inar" operator, since "ou use the arrow operator in state'ents like
my/$ass0tr*>myL$ement. However, C++ has a rather clever 'echanis' for o&erator *> that 'akes it a
unar" operator. ) class%s o&erator *> function should return a pointer to the o.ect that the arrow oper$
ator should actuall" e applied to. +his 'a" e a it confusing, so an e#a'ple is in order. *uppose we have
a class /ustomString0ointer that acts as though it%s a pointer to a C++ string o.ect. +hen if we have
the following code:
/ustomString0ointer my/ustom0ointer;
cout << my/ustom0ointer*>$engt.() << end$;
+his code is e!uivalent to
/ustomString0ointer my/ustom0ointer;
cout << (my/ustom0ointer.o&erator *>())*>$engt.() << end$;
(n the first version of the code, we treated the my/ustom0ointer o.ect as though it was a real pointer "
using the arrow operator to select the $engt. function. +his code e#pands out into two s'aller steps:
3. +he /ustomString0ointer%s o&erator *> function is called to deter'ine which pointer the ar$
row should e applied to.
4. +he returned pointer then has the *> operator applied to select the $engt. function.
+hus when writing the o&erator *> function, "ou si'pl" need to return the pointer that the arrow oper$
ator should e applied to. (f "ou%re writing a custo' iterator class, for e#a'ple, this is proal" the ele$
'ent eing iterator over.
/e%ll e#plore one e#a'ple of overloading these operators in a later chapter.
List of Overloada%le Operators
+he following tale lists the 'ost co''onl"$used operators "ou%re legall" allowed to overload in C++,
along with an" restrictions aout how "ou should define the operator.
Operator Yields Usage
=
5value
%y/$ass9 o&erator =(const %y/$ass9 ot.er);
*ee the the earlier chapter for details.
= *= !=
1= 3=
'etc.(
5value
%y/$ass9 o&erator =(const %y/$ass9 ot.er);
/hen writing co'pound assign'ent operators, 'ake sure that "ou correctl"
handle self$co'pound$assign'ent.
$ ?;@ $ Chapter 10: Operator Overloading
* ! 1 3
'etc.(
6value
const %y/$ass o&erator (const %y/$ass9 one2
const %y/$ass9 t#o);
+hese operator should e defined as a free functions.
< <= ==
> >= !=
6value
boo$ o&erator < (const %y/$ass9 ot.er) const;
boo$ o&erator < (const %y/$ass9 one2
const %y/$ass9 t#o);
(f "ou%re planning to use relational operators onl" for the *+, container classes,
"ou .ust need to overload the < operator. 2therwise, "ou should overload all si# so
that users aren%t surprised that one != t#o is illegal while !(one == t#o) is
defined.
EF
5value
L$em?y&e9 o&erator EF(const Uey?y&e9 key);
const L$em?y&e9 o&erator EF(const Uey?y&e9 key) const;
Aost of the ti'e "ou%ll need a const$overloaded version of the racket operator.
&orgetting to provide one can lead to a real headache!
**
7refi" 5value
7ostfi" 6value
=refi# version: %y/$ass9 o&erator ();
=ostfi# version: const %y/$ass o&erator (int dummy);
*
6value
const %y/$ass o&erator *() const;
!
5value
L$em?y&e9 o&erator !() const;
/ith this function, "ou%re allowing "our class to act as though it%s a pointer. +he
return t"pe should e a reference to the o.ect it%s pointing at. +his is how the
*+, iterators and s'art pointers work. Note that this is the unar" ! operator and
is not the sa'e as the ! 'ultiplicative operator.
*>
5value
L$em?y&e! o&erator *>() const;
(f the *> is overloaded for a class, whenever "ou write my/$ass*>my%ember, it%s
e!uivalent to my/$ass.o&erator *>()*>my%ember. Note that the function
should e const even though the o.ect returned can still 'odif" data. +his has to
do with how pointers can legall" e used in C++. &or 'ore infor'ation, refer to
the chapter on const.
<< >>
5value
friend ostream9 o&erator << (ostream9 out2
const %y/$ass9 mc);
friend istream9 o&erator >> (istream9 in2
%y/$ass9 mc);
()
8aries *ee the chapter on functors.
Extended Example: grid
The 9T5 encompasses a wide selection of associative and se%uence containers. 2owever, one useful data type
that did not find its way into the 9T5 is a multidimensional array class a!in to the C93:;,<= Drid. )n this e-
tended eample, we will implement an 9T5-friendly version of the C93:;,<= Drid class, which we'll call
grid, that will support 9T5-compatible iterators, intuitive element-access synta, and relational operators.
/nce we're done, we'll have an industrial-strength container class we will use later in this boo! to implement
more comple eamples.
('ple'enting a full"$functional grid 'a" see' daunting at first, ut fortunatel" it%s eas" to reak the
work up into several s'aller steps that cul'inate in a working class.
Chapter 10: Operator Overloading $ ?;C $
Step 0: 'mplement the "asi grid Class)
-efore diving into so'e of the grid%s 'ore advanced features, we%ll egin " i'ple'enting the grid a$
sics. -elow is a partial specification of the grid class that provides core functionalit":
Figure 0: asic !inco"plete# interface for the grid class
tem&$ate <ty&ename ?> c$ass grid 8
&ub$ic:
1! /onstructors2 destructors. !1
grid(); 11 /reate em&ty grid
grid(si>e@t ro#s2 si>e@t co$s); 11 /onstruct to s&ecified si>e
1! 7esi>ing o&erations. !1
void c$ear(); 11 Lm&ty t.e grid
void resi>e(si>e@t ro#s2 si>e@t co$s); 11 7esi>e t.e grid
1! Vuery o&erations. !1
si>e@t num7o#s() const; 11 7eturns number of ro#s in t.e grid
si>e@t num/o$s() const; 11 7eturns number of co$umns in t.e grid
boo$ em&ty() const; 11 7eturns #.et.er t.e grid is em&ty
si>e@t si>e() const; 11 7eturns t.e number of e$ements
1! L$ement access. !1
?9 get4t(si>e@t ro#2 int co$); 11 4ccess individua$ e$ements
const ?9 get4t(int ro#2 int co$) const; 11 Same2 but const
:;
+hese functions are defined in greater detail here:
grid#%;
Constructs a new, e'pt" grid.
grid#si'e() ro*s$ si'e() +ols%;
Constructs a new grid with the specified nu'er
of rows and colu'ns. >ach ele'ent in the grid is
initialized to its default value.
,oid +lear#%;
Besizes the grid to 7#7.
,oid resi'e#si'e() ro*s$ si'e() +ols%;
1iscards the current contents of the grid and res$
izes the grid to the specified size. >ach ele'ent in
the grid is initialized to its default value.
si'e() n-m.o*s#% +ons);
si'e() n-m/ols#% +ons);
Beturns the nu'er of rows and colu'ns in the
grid.
bool emp)0#% +ons);
Beturns whether the grid contains no ele'ents.
+his is true if either the nu'er of rows or colu'ns
is zero.
si'e() si'e#% +ons);
Beturns the nu'er of total ele'ents in the grid.
12 ge)3)#si'e() ro*$ si'e() +ol%;
+ons) 12 ge)3)#si'e() ro*$ si'e() +ol% +ons);
Beturns a reference to the ele'ent at the specified
position. +his function is const*overloaded. /e
won%t worr" aout the case where the indices are
out of ounds.
-ecause grids can e d"na'icall" resized, we will need to ack grid with so'e sort of d"na'ic 'e'or"
'anage'ent. -ecause the grid represents a two$di'ensional entit", "ou 'ight think that we need to use
$ ?;E $ Chapter 10: Operator Overloading
a d"na'icall"$allocated 'ultidi'ensional arra" to store grid ele'ents. However, working with d"na'ic$
all"$allocated 'ultidi'ensional arra"s is trick" and greatl" co'plicates the i'ple'entation. &ortunatel",
we can sidestep this prole' " i'ple'enting the two$di'ensional grid o.ect using a single$di'ension$
al arra". +o see how this works, consider the following ?#? grid:
7 ; 5
? @ C
E G H
/e can represent all of the ele'ents in this grid using a one$di'ensional arra" " la"ing out all of the ele$
'ents se!uentiall", as seen here:
7 ; 5 ? @ C E G H
(f "ou%ll notice, in this ordering, the three ele'ents of the first row appear in order as the first three ele$
'ents, then the three ele'ents of the second row in order, and finall" the three ele'ents of the final row
in order. -ecause this one$di'ensional representation of a two$di'ensional o.ect preserves the ordering
of individual rows, it is so'eti'es referred to as row-"ajor order.
+o represent a grid in row$'a.or order, we need to e ale to convert etween grid coordinates and arra"
indices. Liven a coordinate 3row, col4 in a grid of di'ensions 3nrows, ncols4, the corresponding position in
the row$'a.or order representation of that grid is given " index * col $ row % ncols. +he intuition ehind
this for'ula is that ecause the ordering within an" row is preserved, each horizontal step in the grid
translates into a single step forward or ackward in the row$'a.or order representation of the grid. How$
ever, each vertical step in the grid re!uires us to advance forward to the ne#t row in the linearized grid,
skipping over ncols ele'ents.
<sing row$'a.or order, we can ack the grid class with a regular *+, vector, as shown here:
tem&$ate <ty&ename ?> c$ass grid 8
&ub$ic:
grid();
grid(si>e@t ro#s2 si>e@t co$s);
void c$ear();
void resi>e(si>e@t ro#s2 si>e@t co$s);

si>e@t num7o#s() const;
si>e@t num/o$s() const;
boo$ em&ty() const;
si>e@t si>e() const;
?9 get4t(si>e@t ro#2 si>e@t co$);
const ?9 get4t(si>e@t ro#2 si>e@t co$) const;
&rivate:
vector<?> e$ems;
si>e@t ro#s;
si>e@t co$s;
:;
Chapter 10: Operator Overloading $ ?;G $
*erendipitousl", i'ple'enting the grid with a vector allows us to use C++%s auto'aticall"$generated
cop" constructor and assign'ent operator for grid. *ince vector alread" 'anages its own 'e'or", we
don%t need to handle it 'anuall".
Note that we e#plicitl" keep track of the nu'er of rows and colu'ns in the grid even though the vector
stores the total nu'er of ele'ents. +his is necessar" so that we can co'pute indices in the row$'a.or
ordering for points in two$di'ensional space.
+he aove functions have relativel" straightforward i'ple'entations that are given elow:
tem&$ate <ty&ename ?> grid<?>::grid() : ro#s(I)2 co$s(I) 8
:
tem&$ate <ty&ename ?>
grid<?>::grid(si>e@t ro#s2 si>e@t co$s)
: e$ems(ro#s ! co$s)2 ro#s(ro#s)2 co$s(co$s) 8
:
tem&$ate <ty&ename ?> void grid<?>::c$ear() 8
resi>e(I2 I);
:
tem&$ate <ty&ename ?> void grid<?>::resi>e(si>e@t ro#s2 si>e@t co$s) 8
1! See be$o# for assign !1
e$ems.assign(ro#s ! co$s2 L$em?y&e());
1! LA&$icit t.is*> reWuired since &arameters .ave same name as members. !1
t.is*>ro#s = ro#s;
t.is*>co$s = co$s;
:
tem&$ate <ty&ename ?> si>e@t grid<?>::num7o#s() const 8
return ro#s;
:
tem&$ate <ty&ename ?> si>e@t grid<?>::num/o$s() const 8
return co$s;
:
tem&$ate <ty&ename ?> boo$ grid<?>::em&ty() const 8
return si>e() == I;
:
tem&$ate <ty&ename ?> si>e@t grid<?>::si>e() const 8
return num7o#s() ! num/o$s();
:
1! Jse ro#*maMor ordering to access t.e &ro&er e$ement of t.e vector. !1
tem&$ate <ty&ename ?> ?9 grid<?>::get4t(si>e@t ro#2 si>e@t co$) 8
return e$emsEco$ ro# ! co$sF;
:
tem&$ate <ty&ename ?> const ?9 grid<?>::get4t(si>e@t ro#2si>e@t co$) const 8
return e$emsEco$ ro# ! co$sF;
:
$ ?;H $ Chapter 10: Operator Overloading
Aost of these functions are one$liners and are e#plained in the co''ents. +he onl" function that "ou 'a"
find interesting is resi>e, which uses the vector%s assign 'e'er function. assign is si'ilar to res*
i>e in that it changes the size of the vector, ut unlike resi>e assign discards all of the current vector
contents and replaces the' with the specified nu'er of copies of the specified ele'ent. +he use of
L$em?y&e() as the second para'eter to assign 'eans that we will fill the vector with copies of the de$
fault value of the t"pe eing stored 3since L$em?y&e() uses the te'porar" o.ect s"nta# to create a new
L$em?y&e4.
Step 1: Add Support for 'terators
Now that we have the asics of a grid class, it%s ti'e to add iterator support. +his will allow us to plug the
grid directl" into the *+, algorith's and will e invaluale in a later chapter.
,ike the ma& and set, the grid does not naturall" lend itself to a linear traversal 8 after all, grid is two$di$
'ensional 8 and so we 'ust aritraril" choose an order in which to visit ele'ents. *ince we%ve i'ple'en$
ted the grid in row$'a.or order, we%ll have grid iterators traverse the grid row$"$row, top to otto',
fro' left to right. +hus, given a ?#@ grid, the order of the traversal would e
7 ; 5
? @ C
E G H
6 ;7 ;;
+his order of iteration 'aps naturall" onto the row$'a.or ordering we%ve chosen for the grid. (f we con$
sider how the aove grid would e laid out in row$'a.or order, the resulting arra" would look like this:
7 ; 5 ? @ C E G H 6 ;7 ;;
+hus this iteration sche'e 'aps to a si'ple linear traversal of the underl"ing representation of the grid.
-ecause we%ve chosen to represent the ele'ents of the grid using a vector, we can iterate over the ele$
'ents of the grid using vector iterators. /e thus add the following definitions to the grid class:
Chapter 10: Operator Overloading $ ?;6 $
tem&$ate <ty&ename ?> c$ass grid 8
&ub$ic:
grid();
grid(si>e@t ro#s2 si>e@t co$s);
void c$ear();
void resi>e(si>e@t ro#s2 si>e@t co$s);

si>e@t num7o#s() const;
si>e@t num/o$s() const;
boo$ em&ty() const;
si>e@t si>e() const;
?9 get4t(si>e@t ro#2 si>e@t co$);
const ?9 get4t(si>e@t ro#2 si>e@t co$) const;
ty&edef ty&ename vector<?>::iterator iterator;
ty&edef ty&ename vector<?>::const@iterator const@iterator;
&rivate:
vector<?> e$ems;
si>e@t ro#s;
si>e@t co$s;
:;
Now, clients of grid can create grid<int>::iterators rather than vector<int>::iterators. +his
'akes the interface 'ore intuitive and increases encapsulation9 since iterator is a ty&edef, if we later
decide to replace the underl"ing representation with a d"na'icall"$allocated arra", we can change the ty*
&edefs to
ty&edef L$em?y&e! iterator;
ty&edef const L$em?y&e! const@iterator;
)nd clients of the grid will not notice an" difference.
Notice that in the aove ty&edefs we had to use the ty&ename ke"word to na'e the t"pe vector<L$em*
?y&e>::iterator. +his is the pesk" edge case 'entioned in the chapter on te'plates and so'ehow
'anages to creep into 'ore than its fair share of code. *ince iterator is a nested t"pe inside the te'$
plate t"pe vector<L$em?y&e>, we have to use the ty&ename ke"word to indicate that iterator is the
na'e of a t"pe rather than a class constant.
/e%ve now defined an iterator t"pe for our grid, so what functions should we e#port to the grid cli$
ents0 /e%ll at least want to provide support for begin and end, as shown here:
$ ?57 $ Chapter 10: Operator Overloading
tem&$ate <ty&ename ?> c$ass grid 8
&ub$ic:
grid();
grid(si>e@t ro#s2 si>e@t co$s);
void c$ear();
void resi>e(si>e@t ro#s2 si>e@t co$s);

si>e@t num7o#s() const;
si>e@t num/o$s() const;
boo$ em&ty() const;
si>e@t si>e() const;
?9 get4t(si>e@t ro#2 si>e@t co$);
const ?9 get4t(si>e@t ro#2 si>e@t co$) const;
ty&edef ty&ename vector<?>::iterator iterator;
ty&edef ty&ename vector<?>::const@iterator const@iterator;
iterator begin();
const@iterator begin() const;
iterator end();
const@iterator end() const;
&rivate:
vector<?> e$ems;
si>e@t ro#s;
si>e@t co$s;
:;
/e%ve provided two versions of each function so that clients of a const grid can still use iterators. +hese
functions are easil" i'ple'ented " returning the value of the underl"ing vector%s begin and end func$
tions, as shown here:
tem&$ate <ty&ename ?> ty&ename grid<?>::iterator grid<?>::begin() 8
return e$ems.begin();
:
Notice that the return t"pe of this function is ty&ename grid<L$em?y&e>::iterator rather than .ust
iterator. -ecause iterator is a nested t"pe inside grid, we need to use grid<L$em?y&e>::iterator
to specif" which iterator we want, and since grid is a te'plate t"pe we have to use the ty&ename
ke"word to indicate that iterator is a nested t"pe. 2therwise, this function should e straightforward.
+he rest of the functions are i'ple'ented here:
tem&$ate <ty&ename ?> ty&ename grid<?>::const@iterator grid<?>::begin() const 8
return e$ems.begin();
:
tem&$ate <ty&ename ?> ty&ename grid<?>::iterator grid<?>::end() 8
return e$ems.end();
:
tem&$ate <ty&ename ?> ty&ename grid<?>::const@iterator grid<?>::end() const 8
return e$ems.end();
:
Chapter 10: Operator Overloading $ ?5; $
-ecause the grid is i'ple'ented in row$'a.or order, ele'ents of a single row occup" consecutive loca$
tions in the vector. (t%s therefore possile to return iterators delineating the start and end of each row in
the grid. +his is useful functionalit", so we%ll provide it to clients of the grid through a pair of 'e'er
functions ro#@begin and ro#@end 3plus const overloads4. +hese functions are declared here:
tem&$ate <ty&ename ?> c$ass grid 8
&ub$ic:
grid();
grid(si>e@t ro#s2 si>e@t co$s);
void c$ear();
void resi>e(si>e@t ro#s2 si>e@t co$s);

si>e@t num7o#s() const;
si>e@t num/o$s() const;
boo$ em&ty() const;
si>e@t si>e() const;
?9 get4t(si>e@t ro#2 si>e@t co$);
const ?9 get4t(si>e@t ro#2 si>e@t co$) const;
ty&edef ty&ename vector<L$em?y&e>::iterator iterator;
ty&edef ty&ename vector<L$em?y&e>::const@iterator const@iterator;
iterator begin();
const@iterator begin() const;
iterator end();
const@iterator end() const;
iterator ro#@begin(si>e@t ro#);
const@iterator ro#@begin(si>e@t ro#) const;
iterator ro#@end(si>e@t ro#);
const@iterator ro#@end(si>e@t ro#) const;

&rivate:
vector<?> e$ems;
si>e@t ro#s;
si>e@t co$s;
O9
-efore i'ple'enting these functions, let%s take a 'inute to figure out e#actl" where the iterations we re$
turn should point to. Becall that the ele'ent at position 3row, 74 in a grid of size 3rows, cols4 can e found
at position row F cols. /e should therefore have ro#@begin(ro#) return an iterator to the ro# F cols ele$
'ent of the vector. *ince there are cols ele'ents in each row and ro#@end should return an iterator to
one position past the end of the row, this function should return an iterator to the position cols past the
location returned " ro#@begin. Liven this infor'ation, we can i'ple'ent these functions as shown
here:
tem&$ate <ty&ename ?> ty&ename grid<?>::iterator grid<?>::ro#@begin(int ro#) 8
return begin() num/o$s() ! ro#;
:
tem&$ate <ty&ename ?>
ty&ename grid<?>::const@iterator grid<?>::ro#@begin(int ro#) const 8
return begin() num/o$s() ! ro#;
:
$ ?55 $ Chapter 10: Operator Overloading
tem&$ate <ty&ename ?> ty&ename grid<?>::iterator grid<?>::ro#@end(int ro#) 8
return ro#@begin(ro#) num/o$s();
:
tem&$ate <ty&ename ?>
ty&ename grid<?>::const@iterator grid<?>::ro#@end(int ro#) const 8
return ro#@begin(ro#) num/o$s();
:
/e now have an elegant iterator interface for the grid class. /e can iterate over the entire container as
a whole, .ust one row at a ti'e, or so'e co'ination thereof. +his enales us to interface the grid with
the *+, algorith's. &or e#a'ple, to zero out a grid<int>, we can use the fi$$ algorith', as shown here:
fi$$(myDrid.begin()2 myDrid.end()2 I);
/e can also sort the ele'ents of a row using sort:
sort(myDrid.ro#@begin(I)2 myDrid.ro#@end(I));
/ith onl" a handful of functions we%re now capale of plugging directl" into the full power of the al$
gorith's. +his is part of the eaut" of the *+, 8 had the algorith's een designed to work on containers
rather than iterator ranges, this would not have een possile.
Step +: Add Support for the Element Seletion Operator
/hen using regular C++ 'ultidi'ensional arra"s, we can write code that looks like this:
int my4rrayE'()FEBCF;
my4rrayECFEBF = C)'QCQ;
my4rrayEZFEIF = ('B',Z;
However, with the current specification of the grid class, the aove code would e illegal if we replaced
the 'ultidi'ensional arra" with a grid<int>, since we haven%t provided an i'ple'entation of o&erat*
or EF.
)dding support for ele'ent selection to linear classes like the vector is si'ple 8 we si'pl" have the
rackets operator return a reference to the proper ele'ent. <nfortunatel", it is 'uch trickier to design
grid such that the racket s"nta# works correctl". +he reason is that if we write code that looks like this:
grid<int> myDrid('()2 BC);
int va$ue = myDridECFEBF;
-" replacing the racket s"nta# with calls to o&erator EF, we see that this code e#pands out to
grid<int> myDrid('()2 BC);
int va$ue = (myDrid.o&eratorEF (C)).o&eratorEF (B);
Here, there are two calls to o&erator EF, one invoked on myDrid and the other on the value returned "
myDrid.o&eratorEF(C). +o 'ake the aove code co'pile, the o.ect returned " the grid%s
o&eratorEF 'ust itself define an o&erator EF function. (t is this returned o.ect, rather than the grid
itself, which is responsile for retrieving the re!uested ele'ent fro' the grid. *ince this te'porar" o$
.ect is used to perfor' a task nor'all" reserved for the grid, it is so'eti'es known as a proxy object.
Chapter 10: Operator Overloading $ ?5? $
How can we i'ple'ent the grid%s o&erator EF so that it works as descried aove0 &irst, we will need
to define a new class representing the o.ect returned " the grid%s o&erator EF. (n this discussion,
we%ll call it %utab$e7eference, since it represents an o.ect that can call ack into the grid and 'utate
it. &or si'plicit" and to 'a#i'ize encapsulation, we%ll define %utab$e7eference inside of grid. +his
results in the following interface for grid:
tem&$ate <ty&ename ?> c$ass grid 8
&ub$ic:
1! ... &revious$y*defined functions ... !1
c$ass %utab$e7eference 8
&ub$ic:
friend c$ass grid;
?9 o&eratorEF (si>e@t co$);
&rivate:
%utab$e7eference(grid! o#ner2 si>e@t ro#);

grid! const o#ner;
const si>e@t ro#;
:;

&rivate:
vector<?> e$ems;
si>e@t ro#s;
si>e@t co$s;
:;
+he %utab$e7eference o.ect stores so'e a pointer to the grid that created it, along with the inde#
passed in to the grid%s o&erator EF function when the %utab$e7eference was created. +hat wa",
when we invoke the %utab$e7eference%s o&erator EF function specif"ing the col coordinate of the grid,
we can pair it with the stored row coordinate, then !uer" the grid for the ele'ent at 3row, col4. /e have
also 'ade grid a friend of %utab$e7eference so that the grid can call the private constructor neces$
sar" to initialize a %utab$e7eference.
/e can i'ple'ent %utab$e7eference as follows:
tem&$ate <ty&ename ?>
grid<?>::%utab$e7eference::%utab$e7eference(grid! o#ner2 int ro#) :
o#ner(o#ner)2 ro#(ro#) 8
:
tem&$ate <ty&ename ?>
?9 grid<?>::%utab$e7eference::o&eratorEF (int co$) 8
return o#ner*>get4t(ro#2 co$);
:
Notice that ecause %utab$e7eference is a nested class inside grid, the i'ple'entation of the %uta*
b$e7eference functions is prefaced with grid<L$em?y&e>::%utab$e7eference instead of .ust %uta*
b$e7eference. However, in this particular case the pesk" ty&ename ke"word is not necessar" ecause
we are protot"ping a function inside %utab$e7eference rather than using the t"pe %utab$e7eference
in an e#pression.
Now that we%ve i'ple'ented %utab$e7eference, we%ll define an o&erator EF function for the grid
class that constructs and returns a properl"$initialized %utab$e7eference. +his function accepts an row
$ ?5@ $ Chapter 10: Operator Overloading
coordinate, and returns a %utab$e7eference storing that row nu'er and a pointer ack to the grid.
+hat wa", if we write
int va$ue = myDridE'FECF;
+he following se!uences of actions occurs:
3. myDrid.o&eratorEF is invoked with the para'eter ;.
4. myDrid.o&eratorEF creates a %utab$e7eference storing the row coordinate ; and a 'eans for
co''unicating ack with the myDrid o.ect.
>. myDrid.o&eratorEF returns this %utab$e7eference.
?. +he returned %utab$e7eference then has its o&eratorEF function called with para'eter 5.
@. +he returned %utab$e7eference then calls ack to the myDrid o.ect and asks for the ele'ent at
position 3;, 54.
+his se!uence of actions is ad'ittedl" co'ple#, ut is transparent to the client of the grid class and runs
efficientl".
o&eratorEF is defined and i'ple'ented as follows:
tem&$ate <ty&ename ?> c$ass grid 8
&ub$ic:
1! ... &revious$y*defined functions ... !1
c$ass %utab$e7eference 8
&ub$ic:
friend c$ass grid;
?9 o&eratorEF (si>e@t co$);
&rivate:
%utab$e7eference(grid! o#ner2 si>e@t ro#);

grid! const o#ner;
const si>e@t ro#;
:;
%utab$e7eference o&eratorEF (int ro#);

&rivate:
vector<?> e$ems;
si>e@t ro#s;
si>e@t co$s;
:;
tem&$ate <ty&ename ?>
ty&ename grid<?>::%utab$e7eference grid<?>::o&eratorEF (int ro#) 8
return %utab$e7eference(t.is2 ro#);
:
Notice that we%ve onl" provided an i'ple'entation of the non$const version of o&eratorEF. -ut what if
we want to use o&eratorEF on a const grid0 /e would si'ilarl" need to return a pro#" o.ect, ut that
o.ect would need to guarantee that grid clients could not write code like this:
Chapter 10: Operator Overloading $ ?5C $
const grid<int> myDrid('()2 BC);
myDridEIFEIF = C)'Q; 11 Koo&s! %odified const obMect!
+o prevent this sort of prole', we%ll have the const version of o&eratorEF return a pro#" o.ect of a dif$
ferent t"pe, called +mmutab$e7eference which ehaves si'ilarl" to %utab$e7eference ut which re$
turns const references to the ele'ents in the grid. +his results in the following interface for grid:
tem&$ate <ty&ename ?> c$ass grid 8
&ub$ic:
1! ... &revious$y*defined functions ... !1

c$ass %utab$e7eference 8
&ub$ic:
friend c$ass grid;
?9 o&eratorEF (si>e@t co$);

&rivate:
%utab$e7eference(grid! o#ner2 si>e@t ro#);

grid! const o#ner;
const si>e@t ro#;
:;
%utab$e7eference o&eratorEF (int ro#);

c$ass +mmutab$e7eference 8
&ub$ic:
friend c$ass grid;
const ?9 o&eratorEF (si>e@t co$) const;

&rivate:
%utab$e7eference(const grid! o#ner2 si>e@t ro#);

const grid! const o#ner;
const si>e@t ro#;
:;
+mmutab$e7eference o&eratorEF (si>e@t ro#) const;

&rivate:
vector<?> e$ems;
si>e@t ro#s;
si>e@t co$s;
:;
+mmutab$e7eference and the const version of o&eratorEF are si'ilar to %utab$e7eference and the
non$const version of o&eratorEF, and to save space we won%t write it here. +he co'plete listing of the
grid class at the end of this chapter contains the i'ple'entation if "ou%re interested.
Step ,: Define Relational Operators
Now that our grid has full support for iterators and a nice racket s"nta# that lets us access individual
ele'ents, it%s ti'e to put on the finishing touches. )s a final step in the pro.ect, we%ll provide i'ple'enta $
tions of the relational operators for our grid class. /e egin " updating the grid interface to include the
following functions:
$ ?5E $ Chapter 10: Operator Overloading
tem&$ate <ty&ename ?> c$ass grid 8
&ub$ic:
1! ... &revious$y*defined functions ... !1

boo$ o&erator < (const grid9 ot.er) const;
boo$ o&erator <= (const grid9 ot.er) const;
boo$ o&erator == (const grid9 ot.er) const;
boo$ o&erator != (const grid9 ot.er) const;
boo$ o&erator >= (const grid9 ot.er) const;
boo$ o&erator > (const grid9 ot.er) const;

&rivate:
vector<?> e$ems;
si>e@t ro#s;
si>e@t co$s;
:;
Note that of the si# operators listed aove, onl" the == and != operators have intuitive 'eanings when ap$
plied to grids. However, it also 'akes sense to define a < operator over grids so that we can store the'
in *+, ma& and set containers, and to ensure consistenc", we should define the other three operators as
well.
-ecause there is no natural interpretation for what it 'eans for one grid to e less than another, we are
free to i'ple'ent these functions in an" wa" that we see fit, provided that we oe" transitivit" and tricho$
to'". )s 'entioned earlier it is possile to i'ple'ent all si# of the relational operators in ter's of the
less$than operator. 2ne strateg" for i'ple'enting the relational operators is thus to i'ple'ent .ust the
less$than operator and then to define the other five as wrapped calls to o&erator <. -ut what is the est
wa" to deter'ine whether one grid co'pares less than another0 2ne general approach is to define a lex-
icographical ordering over grids. /e will co'pare each field one at a ti'e, checking to see if the fields are
e!ual. (f so, we 'ove on to the ne#t field. 2therwise, we i''ediatel" return that one grid is less than an$
other without looking at the re'aining fields. (f we go through ever" field and find that the grids are
e!ual, then we can return that neither grid is less than the other. +his is si'ilar to the wa" that we 'ight
order words alphaeticall" 8 we find the first 'is'atched character, then return which word co'pares
first. /e can egin " i'ple'enting o&erator < as follows:
tem&$ate <ty&ename ?> boo$ grid<?>::o&erator < (const grid9 ot.er) const 8
1! /om&are t.e number of ro#s and return if t.ereXs a mismatc.. !1
if(ro#s != ot.er.ro#s)
return ro#s < ot.er.ro#s;
1! NeAt com&are t.e number of co$umns t.e same #ay. !1
if(co$s != ot.er.co$s)
return co$s < ot.er.co$s;
1! ... !1
:
Here, we co'pare the ro#s fields of the two o.ects and i''ediatel" return if the" aren%t e!ual. /e can
then check the co$s fields in the sa'e wa". &inall", if the two grids have the sa'e nu'er of rows and
colu'ns, we need to check how the ele'ents of the grids co'pare. &ortunatel", this is straightforward
thanks to the *+, $eAicogra&.ica$@com&are algorith'. $eAicogra&.ica$@com&are accepts four iter$
ators delineating two ranges, then le#icographicall" co'pares the ele'ents in those ranges and returns if
the first range co'pares le#icographicall" less than the second. <sing $eAicogra&.ica$@com&are, we
can finish our i'ple'entation of o&erator < as follows:
Chapter 10: Operator Overloading $ ?5G $
tem&$ate <ty&ename ?> boo$ grid<?>::o&erator < (const grid9 ot.er) const 8
1! /om&are t.e number of ro#s and return if t.ereXs a mismatc.. !1
if(ro#s != ot.er.ro#s)
return ro#s < ot.er.ro#s;
1! NeAt com&are t.e number of co$umns t.e same #ay. !1
if(co$s != ot.er.co$s)
return co$s < ot.er.co$s;
return $eAicogra&.ica$@com&are(begin()2 end()2 ot.er.begin()2 ot.er.end());
:
)ll that%s left to do now is to i'ple'ent the other five relational operators in ter's of o&erator <. +his is
done elow:
tem&$ate <ty&ename ?> boo$ grid<?>::o&erator >=(const grid9 ot.er) const 8
return !(!t.is < ot.er);
:

tem&$ate <ty&ename ?> boo$ grid<?>::o&erator ==(const grid9 ot.er) const 8
return !(!t.is < ot.er) 99 !(ot.er < !t.is);
:

tem&$ate <ty&ename ?> boo$ grid<?>::o&erator !=(const grid9 ot.er) const 8
return (!t.is < ot.er) TT (ot.er < !t.is);
:

tem&$ate <ty&ename ?> boo$ grid<?>::o&erator > (const grid9 ot.er) const 8
return ot.er < !t.is;
:

tem&$ate <ty&ename ?> boo$ grid<?>::o&erator <=(const grid9 ot.er) const 8
return !(ot.er < !t.is);
:
)t this point we%re done! /e now have a co'plete working i'ple'entation of the grid class that sup$
ports iteration, ele'ent access, and the relational operators. +o oot, it%s i'ple'ented on top of the vec*
tor, 'eaning that it%s slick and efficient. +his class should e "our one$stop solution for applications that
re!uire a two$di'ensional arra".
$ ?5H $ Chapter 10: Operator Overloading
!ore to E-plore
2perator overloading is an enor'ous topic in C++ and there%s si'pl" not enough space to cover it all in
this chapter. (f "ou%re interested in so'e 'ore advanced topics, consider reading into the following:
3. Overloaded ne* and dele)e: :ou are allowed to overload the ne# and de$ete operators, in case
"ou want to change how 'e'or" is allocated for "our class. Note that the overloaded ne# and de*
$ete operators si'pl" change how 'e'or" is allocated, not what it 'eans to write ne# %y/$ass.
2verloading ne# and de$ete is a co'plicated task and re!uires a solid understanding of how C++
'e'or" 'anage'ent works, so e sure to consult a reference for details.
4. Conversion funtions: (n an earlier chapter, we covered how to write conversion constructors,
functions that convert o.ects of other t"pes into instances of "our new class. However, it%s pos$
sile to use operator overloading to define an i'plicit conversion fro' o.ects of "our class into
o.ects of other t"pes. +he s"nta# is o&erator ?y&e(), where ?y&e is the data t"pe to convert
"our o.ect to. Aan" professional progra''ers advise against conversion functions, so 'ake sure
that the"%re reall" the est option efore proceeding.
.ratie .ro%lems
2perator overloading is !uite difficult ecause "our functions 'ust act as though the"%re the uilt$in oper$
ators. Here are so'e practice prole's to get "ou used to overloading operators:
3. /hat is an overloaded operator0

4. /hat is an lvalue0 )n rvalue0 1oes the operator "ield an lvalue or an rvalue0 How aout the
pointer dereference operator0

>. )re overloaded operators inherentl" 'ore efficient than regular functions0

?. >#plain how to i'ple'ent o&erator in ter's of o&erator =.

@. /hat is the signature of an overloaded operator for sutraction0 &or unar" negation0

;. How do "ou differentiate etween the prefi# and postfi# versions of the operator0

A. /hat does the *> operator return0

B. /hat is a friend function0 How do "ou declare one0

C. How do "ou declare an overloaded strea' insertion operator0

3:. /hat is trichoto'" and wh" is it i'portant to C++ progra''ers0

33. /hat is transitivit" and wh" is it i'portant to C++ progra''ers0

34. (n ="thon, it is legal to use negative arra" indices to 'ean the ele'ent that 'an" positions fro'
the end of the arra". &or e#a'ple, my4rrayE*'F would e the last ele'ent of an arra",
my4rrayE*CF the penulti'ate ele'ent, etc. <sing operator overloading, it%s possile to i'ple$
'ent this functionalit" for a custo' arra" class. 1o "ou think it%s a good idea to do so0 /h" or
wh" not0 +hink aout the principle of least astonish'ent when answering.
Chapter 10: Operator Overloading $ ?56 $
3>. /h" is it etter to i'ple'ent in ter's of = instead of = in ter's of 0 !&int: 'hin( about the
nu"ber of objects created using += and using +)#
3?. Consider the following definition of a S&an struct:

struct S&an 8
int start2 sto&;
:;

+he S&an struct allows us to define the range of ele'ents fro' Pstart, sto&4 as a single variale.
Liven this definition of S&an and assu'ing start and sto& are oth non$negative, provide anoth$
er racket operator for our Vector class that selects a range of ele'ents.

3@. Consider the following interface for a class that iterates over a container of L$em?y&es:

c$ass iterator 8
&ub$ic:
boo$ o&erator== (const iterator9 ot.er);
boo$ o&erator!= (const iterator9 ot.er);

iterator o&erator ();
L$em?y&e! o&erator! () const;
L$em?y&e! o&erator*> () const;
:;

+here are several 'istakes in the definition of this iterator. /hat are the"0 How would "ou fi#
the'0

3;. +he i'ple'entation of the grid class%s o&erator== and o&erator!= functions i'ple'ented
those operators in ter's of the less$than operator. +his is so'ewhat inefficient, since it%s 'ore
direct to si'pl" check if the two grids are e!ual or une!ual rather than to use the co'parison op$
erators. Bewrite the grid%s o&erator== function to directl" check whether the grids are identic$
al. +hen rewrite o&erator!= in ter's of o&erator==.

Das könnte Ihnen auch gefallen