8 Operators and Enumerated Types

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 59

8 Operators and enumerated

types
Learning Objectives
• 8.1 Overloading operators – the basics
• 8.2 Enumerated types
• 8.3.Overloaded operators in detail
• 8.4 Overloaded operators in detail (continued)
8.1 Overloading operators – the basics
8.1.1 Operators – a glance at the past (1)
• An operator is a symbol designed to operate on data.
• The “C++” language has a wide range of different operators operating on many
different types of data. Some of the operators are more universal, some are more
specific, some of them are written as single symbols, some are di-graphs or even tri-
graphs, other are keywords.
• One of the possible classifications of “C++” language operators is based on a number of
arguments. We know that there are:
• unary operators
• binary operators
• ternary operators (there’s only one ternary operator in the “C++” language)
• Another classification relies on the location of the operator. We distinguish:
• prefix operators (placed in front of their argument)
• postfix operators (placed after their argument)
• infix operators (placed in between their arguments)
8.1.2 Operators – a glance at the past (2)
• How’s it possible that the one and same operator may have such different
traits and operate on arguments so different?
• This is possible by a mechanism similar to function overloading. “C++”
allows the programmer not only to overload functions (to assign a new
implementation to the name of an already existing function) but also to
overload operators.
• Fortunately, the programmer isn’t allowed to change the existing
operator's meaning (e.g. you can’t force “+” to subtract ints or floats) but
you can define new domains for it (e.g. strings are a new domain for “+”).
• Note that “C++” doesn’t allow you to define completely new operators
(e.g. you may not define an operator like this: “$#$”). You only can redefine
any of the existing operators.
8.1.3 What do we want to achieve?
• We’re going to add some new operators to an existing class, as this is
the primary application of the operator overloading mechanism.
• We’ll try to give our stack some new ways access its functionalities.
we want to create a new method of invoking those already
implemented.
• we want to overload the “<<” and “>>” operators.
• Stack stack(100);
• int var; stack << 200; // push
• stack >> var; // pop
• We want the “<<” to be a synonym of the push method invocation
and we want the “>>” to play the role of a pop member function.
8.1.3 What do we want to achieve?
• The “C++” language treats overloaded operators as very specific
functions.
• The number of parameters of these functions must correspond to the
number of operator arguments.
• The name of such a specific function is consists of a keyword “operator”
glued to an operator symbol, e.g. a function implementing the “>>”
operator will be named: operator>>
• An operator function may be implemented in two ways:
• as a member function of a class – it’s implicitly assumed that an object of that class
is one of the required operator’s arguments
• as a “standalone” function – the function must explicitly specify the types of all its
arguments
8.1.4 Implementing the << operator (1)
• void operator<< (int v) throw(stack_overflow);
• Note:
• the operator must accept different forms of its arguments, like:
• variable, e.g. stack << VAR;
• expression, e.g. stack << 2 * VAR;
• literal, e.g. stack << 2;
• etc
• this means that the corresponding parameter of the operator function must be passed
by value
• the operator should be able to throw the same exceptions as the member function it
uses; therefore, we declare the function in the same way as the push function
• we’ve declared the operator function as void, as we don’t want the operator to evaluate
any new values;
Implementing the << operator and >> operator
void Stack::operator<< (int v) throw (stack_overflow) {
push(v);
}

void Stack::operator>> (int &v) throw(stack_empty) {


v = pop();
}
8.2 Enumerated types
8.2.1 Enumerated types – why do we need
them? (1)
• We’ll have to choose a method to represent the week-days and to
manipulate them. Of course, we can use the int type for that purpose
and assume that 0 means Sunday, 1 means Monday etc. Is that smart
enough?
• No, it isn’t.
• You or any of your co-workers will forget the system one day and
won’t be sure if 5 is Friday or not. Digits don’t make good
associations when you operate on weekdays and each assignment is
neither sufficiently obvious nor clear.
8.2.2 Enumerated types – why do we need
them? (2)
The “C++” language pre-processor offers a method to create symbols which will
be replaced by their values during compilation time.
The #define directive has the following syntax:
#define symbol string #define SUNDAY 0
where: #define MONDAY 1
• the symbol is an arbitrary chosen name built like any variable’s or function’s #define TUESDAY 2
name; the unofficial but respected convention says that the symbol should #define WEDNESDAY 3
contain upper-case letters only, to be easily distinguished from regular variables #define THURSDAY 4
(our symbols obey this convention) #define FRIDAY 5
• the string is just a series of characters #define SATURDAY 6
• the pre-processor will automatically replace each occurrence of the symbol with
the string, but don’t forget that this process occurs during compilation time only
and its effects are temporary – your source file remains untouched.
8.2.4 Enumerated types– how do we define them?
enum weekday {Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday};

• the enum keyword begins the declaration of the type


• 'weekday' is the name of the new type being created; the name of the type must
obey the rules regarding names in general
• next goes a list of all of the values creating the new type, separated by commas and
enclosed in curly brackets
• the compiler will implicitly assign the value of 0 to the first element of the list
• any symbol except the first one will be assigned a value greater by one than the
previous element in the list

This means that the SUNDAY symbol is assigned the value of 0, MONDAY –1, TUESDAY – 2, etc.
8.2.5 Enumerated types – how do we use
them? (1)
• The enumerated type is treated in a very specific way. When a value
of the type is assigned to any int value, everything is OK and the
compiler accepts it without reservations.
• int day = SUNDAY;
• In general, any enum type value is implicitly promoted to
the int type when used in a context requiring integral values e.g.
when used in conjunction with operators like +, -, etc.

enum weekday {Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday};


8.2.6 Enumerated types – how do we use
them? (2)
weekday day = Wednesday; //OK
int day = SUNDAY; //is OK
weekday day = 0; //?? invalid conversion from 'int' to 'weekday'
When the enumerated type plays the role of an l-value, the situation
changes. Assigning an int value to it will provoke a compilation warning as
the compiler recognizes these assignments as a potential risk to data
integrity.
You may have to modify the assignment in the following way:
weekday day = static_cast<weekday>(0);
or use an alternative way of type-casting like this:
weekday f = (weekday)0;

enum weekday {Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday};


8.2.7 Enumerated types – how do we use
them? (3)
enum Symbols {ALPHA = -1, BETA = 1, GAMMA};
Any of the elements of the enum type list may be followed by the ‘=’
sign and an expression resulting in an int value. In this case, the symbol
will be assigned the value specified by the expression (default rules are
omitted here).
In the example here, the Symbols enumerated type represents the
following values:
ALPHA: -1
BETA: 1
GAMMA: 2
8.2.8 Enumerated types – how do we use
them? (4)
• More than one symbol of the enum type may have been assigned
with the same value; in other words, some (or even all) symbols may
represent identical values.
• In the following example → enum letters {A = 1, B = 0, C, D};
• the A and C symbols represent the same value: 1.
8.2.9 Enumerated types – how do we use
them? (5)
enum letters {A = 1, B = 0, c, D, A = 1}
On the other hand, all symbols in the list must be unique,
even if they’re assigned the same value.
The example here → enum letters {A = 1, B = 0, C, D, A = 1}
is invalid and will cause a compilation error.
8.2.10 Enumerated types – #include <iostream>
using namespace std;
how do we use them? (6) class Animals {
public:
In general, enum type symbols must be unique enum names {DOG, CAT, CHUPACABRA};
across a namespace, i.e. two different enum types };
can’t use identical symbols.
The example here class Commands {
enum Animals {DOG, CAT, CHUPACABRA}; public:
enum Commands {LS, CD, CAT}; enum names {LS, CD, CAT};
//is invalid too. };
You can avoid this conflict by putting one or both
of the conflicting enum types inside a separate int main(void) {
class/classes, like in the example. We can say that Animals::names a = Animals::CAT;
both CATs now live in different worlds and don’t Commands::names c = Commands::CAT;
disturb each other. return 0;
}
8.3.Overloaded operators in detail
The number of arguments
of the operator you want
to overload

1 2
The way the as a global 1 2
operator is (standalone)
implemented function
as a class 1 1
member function
8.3.2 Number of arguments
• The number of arguments of the overloaded operator
function is strictly restricted and it’s precisely defined
by the context in which the function exists. Two aspects
are decisive:
• the location in which the operator function is defined
• the operator it overloads
Note that in a case in which the
operator function is a class member
function, the object of the class is
recognized as one of the required
arguments (usually the left one).
There’s one exception to the above
rules.
8.3.3 What you mustn’t do
Don’t forget that you’re not allowed to:
• define new operators (those that are not known in the “C++”
language)
• change the priority of the redefined operators
• overload operators working with standard data types
• Now we’re going to show you all the overloadable operators. We’ll
also show you some important requirements and limitations for each
of them.
SURCHARGE DES OPERATEURS
ARITHMETIQUES
Ce type de surcharge est très souvent utilisé et reste très pratique.
L’écriture de la surcharge d’un opérateur passe par une fonction dont
le nom est operator, suivi du symbole d’opération. La liste des
arguments est identique à celle du constructeur par copie de la
classe ; ils sont passés par référence et sont du type de la classe.
#include <iostream> 8.3.4 Arithmetic operators
using namespace std;
class V {
public:
float vec[2];
V(float a0, float a1) { vec[0]=a0; vec[1]=a1; }
V operator+(V &arg) {
V res(0.0f,0.0f);
for(int i = 0; i < 2; i++)
res.vec[i] = vec[i] + arg.vec[i];
return res; int main(void) {
} V v1(0.0f, 1.0f), v2(1.0f, 0.0f), v3(0.0f, 0.0f);
}; float x;
float operator*(V &left, V &right) { v3 = v1 + v2;
float res = 0.0; x = v1 * v2;
for(int i = 0; i < 2; i++) cout << "(" << v3.vec[0] << ", " << v3.vec[1] << ")" << endl;
res += left.vec[i] * right.vec[i]; cout << x << endl;
return res; return 0;
} }
#include <iostream>
using namespace std;
8.3.5 Bitwise operators All of the above operators,
except the ~, are binary.

class V {
public:
int vec[2];
V(int a0, int a1) { vec[0]=a0; vec[1]=a1; }
V operator>>(int arg) {
V res(vec[0],vec[1]);
for(int i = 0; i < 2; i++)
res.vec[i] >>= arg;
return res; int main(void) {
} V v(15, 7);
}; v = v >> 1;
int operator~(V &arg) { cout << "(" << v.vec[0] << ", " << v.vec[1] << ")" << endl;
int res = 1; cout << ~v << endl;
for(int i = 0; i < 2; i++) return 0;
res *= arg.vec[i]; }
return res;
}
SURCHARGE DE L'OPERATEUR D'AFFECTATION
• Parmi l’ensemble des opérateurs, l’opérateur d'affectation = est le
plus utilisé lors du développement d’un programme. Son rôle
principal est de transférer le contenu d’une variable ou d’un objet
dans un autre.
• Sa surcharge est plus délicate que pour les autres opérateurs car il
faut tester, lors de l’affectation, que le transfert s’effectue sur des
objets différents. Dans le cas contraire, cela conduirait à une situation
anachronique de l’affectation d’un objet à lui-même.
• Il faut retenir qu’une initialisation fait appel à un constructeur alors
qu’une affectation fait appel à l’opérateur d'affectation.
int main(void) {
V v1(4, 8), v2, v3;
8.3.6 Assignment operator
#include <iostream> v2 = v3 = v1;
using namespace std; cout << "(" << v2.vec[0] << ", " << v2.vec[1] << ")" << endl;
class V { return 0;
public: }
int vec[2];
V(int a0, int a1) { vec[0]=a0; vec[1]=a1; }
V(void) { vec[0]=vec[1]=0; }
V& operator=(V &arg) {
for(int i = 0; i < 2; i++)
The example here shows an
vec[i] = arg.vec[1 - i]; assignment which swaps the
return *this; vector being assigned: not very
useful and not rather funny, but
} very illustrative.
};
int main(void) {
V v1(4, 8), v2;
v2 = v1;
cout << "(" << v2.vec[0] << ", " << v2.vec[1] << ")" << endl;
return 0;
}
SURCHARGE DES OPERATEURS RELATIONNELS
Les six opérateurs relationnels ==, !=, <, <=, >, >=
peuvent aussi être surchargés. Ils retournent le
plus souvent un type entier (int) qui représente
une valeur vraie (1) ou fausse (0.
Note: overloading one of the operators has absolutely no impact
on the others, e.g. redefining the meaning of the == operator doesn’t
implicitly redefine the != operator, and vice versa. Overloading the < 8.3.7 Relational operators
operator will automatically overload neither the > nor
the >= operators. All of the above operators are binary and
all of them return a Boolean value

#include <iostream>
using namespace std;
class V {
public:
int vec[2];
V(int a0, int a1) { vec[0]=a0; vec[1]=a1; }
bool operator==(V &arg) {
for(int i = 0; i < 2; i++)
if(vec[i] != arg.vec[i]) int main(void) {
return false; V v1(4, 8), v2(3, 7);
the second is a little tricky: it
return true; checks the “greater than” cout << (v1 == v2 ? "true" : "false") << endl;
} relation using the total sum of
cout << (v1 > v2 ? "true" : "false") << endl;
the vector elements.
}; return 0;
bool operator>(V &l, V &r) { }
return l.vec[0]+l.vec[1] > r.vec[0]+r.vec[1];
}
Note: the original (not overloaded) binary operators use a
short-circuit evaluation tactic which means that they try to
omit an evaluation of the right argument if the left argument is
8.3.8 Logical operators
sufficient to determine the final result.

#include <iostream> All of the above operators (except !) - are binary and
return a Boolean value evaluated in the sense of the
#include <cmath> Bool’s algebra.
using namespace std;
class V {
public: Left and right vectors have at least
int vec[2]; one non-zero element.
V(int a0, int a1) { vec[0]=a0; vec[1]=a1; }
bool operator&&(V &arg) {
return abs(vec[0]) + abs(vec[1]) > 0 &&
abs(arg.vec[0]) + abs(arg.vec[1]) > 0;
} int main(void) {
All vector elements are non-zero.
}; V v1(4, 8), v2(3, 7);
bool operator!(V &v) { cout << (v1 && v2 ? "true" : "false") << endl;
return v.vec[0] * v.vec[1] != 0; cout << (!v1 ? "true" : "false") << endl;
} return 0;
}
la syntaxe d’implémentation de l'opérateur de
surcharge
Opérateurs arithmétiques d’affectation :
nomclasse operator+=(const nomclasse&)
nomclasse operator-=(const nomclasse&)
nomclasse operator*=(const nomclasse&)
nomclasse operator/=(const nomclasse&)
nomclasse operator%=(const nomclassek)
#include <iostream>
using namespace std; 8.3.9 Compound assignment operators
All the operator functions from this category
class V { have one argument and directly modify their
home object. The function should return a
public: reference to the modified object to allow
int vec[2]; chaining.

V(int a0, int a1) { vec[0]=a0; vec[1]=a1; }


V& operator+=(V &arg) { Toutes les classes ont un pointeur caché qui a
for(int i = 0; i < 2; i++) pour nom : this. Ce pointeur sur l’objet est
accessible à l’intérieur de la fonction membre.
vec[i] += arg.vec[i]; De ce fait, *this représente l’objet lui-même.
return *this;
} int main(void) {
}; V v1(0, 0), v2(1, 2), v3(3, 4);
V& operator+(V &left, V &right) { v1 = v2 + v3;
V *res = new V(0, 0); v1 += v1;
for(int i = 0; i < 2; i++) cout << "(" << v1.vec[0] << ", " << v1.vec[1] << ")" << endl;
res->vec[i] = left.vec[i] + right.vec[i]; return 0;
return *res; }
}
Note that none of the above operators is implicitly derived from its regular counterpart and
vice versa. Overloading the + operator doesn’t produce the += operator and so on.
la syntaxe d’implémentation de l'opérateur de
surcharge
Opérateurs d’incrémentation, décrémentation :
nomclasse operator++ ( ) ; pour le type préfixé
nomclasse operator++ (int) ; pour le type postfixé
nomclasse operator--() ; pour le type préfixé
nomclasse operator--(int) ; pour le type postfixé
#include <iostream>
using namespace std;
8.3.10 Prefix increment and
class V {
public:
decrement operators
int vec[2]; The prefix form of the ++/-- has to be implemented
V(int a0, int a1) { vec[0]=a0; vec[1]=a1; } as a parameter-less operator function and (as it
V& operator++(void) { modifies its argument before it’s used) it should
for(int i = 0; i < 2; i++) return a reference to the modified object.
You’re responsible for ensuring that the prefix and
vec[i]++; suffix operators leave their arguments in the same
return *this; state and that both are coherent with +=1 and -=1.
} The compiler isn’t shrewd enough to do it for you.
};
int main(void) {
V v1(1, 2);
++v1;
cout << "(" << v1.vec[0] << ", " << v1.vec[1] << ")" << endl;
return 0;
}
8.3.11 Postfix increment and
#include <iostream>
using namespace std; decrement operators
class V { Operators ++ --
public: May be implemented NO
int vec[2]; as global function?
V(int a0, int a1) { vec[0]=a0; vec[1]=a1; }
May be implemented YES
V operator++(int none) { as a member function?
V v(vec[0],vec[1]);
for(int i = 0; i < 2; i++) Type of return value A reference to an object
++vec[i]; or an l-value in general
return v;
The postfix form of the ++/-- has to be implemented as a one-parameter operator
} function (note that the parameter of type int is a complete dummy and you mustn’t use
}; it within the function) and since it serves the object before it’s affected by the
int main(void) { modification, it should return a copy of the unmodified object.
The presence of the dummy int parameter is the only trait that allows the compiler
V v1(2, 3); to distinguish between prefix and postfix overloaded operators.
v1++;
cout << "(" << v1.vec[0] << ", " << v1.vec[1] << ")" << endl;
return 0;
}
8.4 Overloaded operators in detail (continued)
#include <iostream>
#include <stdexcept> 8.4.1 Subscript operator
using namespace std; int main(void) {
class Arr { Arr arr;
private:
int a, b, c, d; for(int i = 1; i <= 4; i++)
public: arr[i] = i * i;
Arr() { a = b = c = d = 0; } for(int i = 4; i > 0; i--)
int& operator[] (int index) { cout << arr[i] << endl;
switch(index) { return 0;
case 1: return a; }
case 2: return b;
case 3: return c; Arr class is an array without an array. That
case 4: return d; pseudo-array accepts indexes starting from 1, not
default: throw range_error("bad index"); from 0
}
} The original form of the [] operator is very complex, in both syntax and semantic aspects. Fundamentally, it requires two
arguments, a pointer (because the name of an array is interpreted as a pointer to its first element) and an int value as
}; an index. Moreover, the original form of the operator is commutative, i.e. in the following declaration
int arr[10];
both of these assignments are valid:
arr[0] = 0;
1[arr] = 1;
#include <iostream>
using namespace std; 8.4.2 Function invocation operator
class Fun {
public:
int operator() (int a1, int a2) {
return a1 > a2 ? a1 : a2;
}
int operator() (int a1, int a2, int a3) {
return a1 > a2 ? (a1 > a3 ? a1 : a3) : (a2 > a3 ? a2 : a3);
}
}; The function invocation operator is very different. Here are some of its
int main(void) { characteristics:
• the number of its parameters isn’t predefined; you can use as
Fun f; many parameters as you want.
• the return type is neither predefined nor suggested; use the
cout << f(1,2) << endl; one you need.
• one class may contain more than one overloaded
cout << f(1,2,3) << endl; operator () function; specify as many as you need, but remember
return 0; to keep them all distinguishable by the number and types of
} parameters (relate it to the requirements for overloaded functions).

The operator may be used to create objects that pretend to be functions.


The example program shows this type of imitating class. It overloads the () operator twice to create a guise for two
overloaded functions, finding a maximal of their arguments.
#include <iostream> P& operator*(string s) { 8.4.3 Pointer
P *p;
#include <string>
using namespace std; if(!s.compare("alpha")) operators
class P { p = new P(0);
public: else if(!s.compare("bravo"))
int no; p = new P(1);
P(int n) : no(n) { } else if(!s.compare("charlie"))
P() : no(0) { } p = new P(2);
string operator&() { else
switch(no) { p = new P(-1);
case 0: return "alpha"; return *p; The & operator overloaded inside the P class
case 1: return "bravo"; } creates a new “pointer”, representing an object
case 2: return "charlie"; with the selected no field value, while the
} int main(void) { overloaded * operator creates a new object
P p1(2); based on the dereferenced “pointer”
} (string actually) value.
}; string s = &p1;
P p2 = *s;
cout << "'" << s << "' -> " << p2.no << endl;
return 0;
}
8.4.4 Other overloadable operators
All the operators listed here, are overloadable in the “C++” language.
Operator ,
Operators ->
Operator new
operator new[]
operator delete
operator delete[]
operator typename
The operators listed here, are non-overloadable in the “C++” language.
?:
.
::
sizeof
8.4.6 Fraction – a class with overloaded
operators
#ifndef __FRACTION_H__
#define __FRACTION_H__
#include <stdexcept> fraction.h
#include <string>
class Fraction {
private:
int numerator, denominator;
int LCM(int x, int y); Note:
int GCD(int x, int y); • LCM is short for Lowest Common Multiplier
public: • GCD is short of Greatest Common Divisor
Fraction(); • a function converting a fraction into a string is called GetString
Fraction(int n); • a function converting a fraction into a double value is called GetValue
Fraction(int n,int d) throw(std::domain_error);
std::string GetString(void); The implementations of the LCM
double GetValue(void); and GDM algorithms are classic
Fraction operator!(void);
Fraction operator+(Fraction arg);
Fraction operator*(Fraction arg);
Fraction operator/(Fraction arg) throw(std::domain_error);
Fraction& operator+=(Fraction arg);
};
std::ostream& operator<< (std::ostream &ostr, Fraction &f);
#endif
#include <iostream>
#include <sstream>
Fraction.cpp 1/3
#include "fraction.h"
using namespace std;
int Fraction::LCM(int x, int y) {
int i = y; Fraction::Fraction() : numerator(0), denominator(1) {}
while(y % x) Fraction::Fraction(int n) : numerator(n), denominator(1) {}
y += i;
return y; Fraction::Fraction(int n,int d) throw(domain_error) :
} numerator(n), denominator(d) {
int Fraction::GCD(int x, int y) { if(denominator == 0)
for(;;) { throw domain_error("bad fraction");
x %= y; }
if(!x) string Fraction::GetString(void) {
return y; ostringstream os;
y %= x; os << "[" << numerator << "/" << denominator << "]";
if(!y) return os.str();
return x; }
}
}
double Fraction::GetValue(void) {
return double(numerator) / double(denominator);
}
Fraction Fraction::operator!(void) { Fraction.cpp 2/3
int gcd = GCD(numerator, denominator);
return Fraction(numerator / gcd, denominator / gcd);
}
Fraction Fraction::operator+(Fraction arg) {
int common_denom = LCM(denominator, arg.denominator);
int numera = numerator * common_denom / denominator +
arg.numerator * common_denom / arg.denominator;
Fraction f(numera, common_denom);
return f;
}
Fraction Fraction::operator*(Fraction arg) {
int numera = numerator * arg.numerator;
int denomi = denominator * arg.denominator;
Fraction f(numera, denomi);
return !f;
}
Fraction Fraction::operator/(Fraction arg) throw(domain_error) {
if(arg.numerator == 0)
throw domain_error("division by zero");
int numera = numerator * arg.denominator;
int denomi = denominator * arg.numerator;
Fraction f(numera, denomi);
Fraction.cpp 3/3
return !f;
}
Fraction& Fraction::operator+=(Fraction arg) {
int common_denom = LCM(denominator, arg.denominator);
int numera = numerator * common_denom / denominator +
arg.numerator * common_denom / arg.denominator;
numerator = numera;
denominator = common_denom;
return *this;
}
ostream& operator<< (ostream &ostr, Fraction &f) {
return ostr << f.GetString();
}
#include "fraction.h"
#include "fraction.cpp"
#include <iostream>
tester.cpp
using namespace std;
int main(void) {
Fraction f1(1,2), f2(2,3), f;
cout << f1 << "->" << f1.GetValue() << endl;
cout << f2 << "->" << f2.GetValue() << endl;
f = f1 + f2;
cout << f1 << "+" << f2 << "=" << f << endl;
f = f2 + f2 + f2;
cout << f2 << "+" << f2 << "+" << f2 << "=" << f << endl;
f = !f;
cout << f2 << "+" << f2 << "+" << f2 << "=" << f << endl;
f = f1 * f2;
cout << f1 << "*" << f2 << "=" << f << endl;
f = f1 / f2;
cout << f1 << ":" << f2 << "=" << f << endl;
Fraction f3(7,8);
f3 += f1;
cout << f3 << endl;
return 0;
}
Assessments chap 8 Operators and
enumerated types
#include <iostream>
using namespace std; Q1
class Int {
public:
int v;
Int(int a) { v = a; }
};
Select correct answer (single choice)
int main() { • It prints 2
Int i = 1; • It prints 0
cout << i; • It prints 1
return 0; • Compilation fails
}
#include <iostream>
using namespace std; Q2
class Int {
public:
int v;
Int(int a) { v = a; }
};
ostream & operator <<(Int &a) {
return cout << a.v; Select correct answer (single choice)
} • It prints 1
int main() { • It prints 2
Int i = 1; • It prints 0
cout << i; • Compilation fails
return 0;
Rappel
}
#include <iostream>
using namespace std; Q3
class Int {
public:
int v;
Int(int a) { v = a; }
};
ostream &operator <<(ostream &o, Int &a) {
return o << --a.v;
}
int main() { Select correct answer (single choice)
Int i = 1; • It prints 0
cout << i; • It prints 2
return 0; • It prints 1
} • Compilation fails
#include <iostream> ostream &operator <<(ostream &o, Int &a) {
using namespace std; a.v++;
class Int { return o << a.v; Q4
public: }
int v; int main() {
Int(int a) { v = a; } Int i = 0;
Int &operator--() { --i ; i--;
++v; cout << i << i;
return *this; return 0;
} }
Int &operator--(int v) { Select correct answer (single choice)
v+=2; • It prints 10
return *this; • It prints 23
} • Compilation fails
}; • It prints 21
#include <iostream> ostream &operator <<(ostream &o, Int &a) {
using namespace std; return o << a.v;
class Int { }
Q5
public: int main() {
int v; Int i = 0;
Int(int a) { v = a; } i++;
Int &operator++(int x) { cout << i << i.v;
v+=2; return 0;
return *this; }
} Select correct answer (single choice)
}; • It prints 20
• Compilation fails
• It prints 22
• It prints 21
#include <iostream>
using namespace std; Q6
class Int {
public:
int v;
Int(int a) { v = a; }
Int &operator[](int x) {
v+=x;
return *this;
}
};
ostream &operator <<(ostream &o, Int &a) {
return o << a.v;
}
int main() { Select correct answers (multiple choice)
Int i = 2; • It prints 24
cout << i.v ; • Compilation fails
cout << i[2];
return 0; • It prints 33
} • It prints 22
#include <iostream>
using namespace std;
enum T { A = 2, B = -1, C }; Q7
class Int {
public:
T v;
Int(T a) { v = a; }
Int & operator++() { v = C; return *this; }
};
ostream &operator <<(ostream &o, Int &a) {
++a;
return o << a.v;
} Select correct answer (single choice)
int main() { • It prints C
Int i = B; • It prints 1
cout << i; • It prints 0
return 0; • Compilation fails
}
#include <iostream>
using namespace std;
enum T { A = 2, B = -1, C }; Q8
class Int {
public:
T v;
Int(T a) { v = a; }
Int & operator++() { v += 2; return *this; }
};
ostream &operator <<(ostream &o, Int &a) {
++a;
return o << a.v;
} Select correct answer (single choice)
int main() { • It prints 0
Int i = B; • Compilation fails
cout << i; • It prints 1
return 0; • It prints 2
}
#include <iostream>
using namespace std; Q9
class N {
public:
float x;
N() { x = 0.0; }
N(float a) { x = a; }
N(N &n) { x = n.x; }
N &operator=(float f) { x = f - 1; return *this; }
};
int main() { Select correct answer (single choice)
N a; • It prints 0
a = 2.0; • It prints 2
cout << a.x; • It prints 1
return 0; • Compilation fails
}
#include <iostream>
#include <string>
using namespace std; Q10
class N {
public:
float x;
N() { x = 0.0; }
N(float a) { x = a; }
N(N &n) { x = n.x; }
string operator==(float f) { if(int(x) == int(f)) return "true"; else return "false"; }
};

int main() {
N a(1.1); Select correct answer (single choice)
cout << (a == 1.9); • Compilation fails
return 0; • It prints an empty string
} • It prints false
• It prints true
Fin
SURCHARGE DES OPERATEURS D'ENTRÉE-SORTIE
• Les opérateurs d'insertion >> et d’extraction << dans le flux, aussi appelés
opérateurs d’entrée-sortie, sont très souvent surchargés par les
développeurs qui leur confèrent alors une personnalisation adaptée aux
traitements en cours.
• Cette opération de surcharge va devoir utiliser des classes existantes au
sein du fichier d’en-tête iostream, qui sont ostream pour l’extraction de
flux et istream pour l’insertion.
• Les paramètres d’entrée comme la valeur de retour seront passés par
référence. Ces opérateurs surchargés seront des fonctions amies.

ostream& operator<< (ostream& ostr, const classe1 a);


istream& operator>> (istream& istr, const classe1 a);

You might also like