You are on page 1of 18

Kuliah 6

PBO
Function Overloading
(Continuing)

function overloading provides a mechanism to create and resolve function calls to multiple
functions with the same name, so long as each function has a unique function prototype.
This allows you to create variations of a function to work with different data types, without
having to think up a unique name for each variant.

https://www.learncpp.com/cpp-tutorial/overloading-the-subscript-operator/
Overloading unary operators +, -, and !
#include <iostream>
Unlike the operators you’ve seen so far that is binary, the
positive (+), negative (-) and logical not (!) operators all are class Cents
unary operators, which means they only operate on one { Coba 1, run code ini bahas hasilnya
operand. private:
int m_cents {};
Because they only operate on the object they are applied to,
typically unary operator overloads are implemented as public:
member functions. Cents(int cents): m_cents(cents) {}
All three operands are implemented in an identical manner.
Let’s take a look at how we’d implement operator- on the // Overload -Cents as a member function
Cents class we used in a previous example: Cents operator-() const;

int getCents() const { return m_cents; }


This should be straightforward. Our overloaded negative };
operator (-) is a unary operator implemented as a member
function, so it takes no parameters (it operates on the *this // note: this function is a member function!
object). It returns a Cents object that is the negation of the Cents Cents::operator-() const
original Cents value. {
return {-m_cents};
Because operator- does not modify the Cents object, we can // since return type is a Cents,
(and should) make it a const function (so it can be called on // this does an implicit conversion from int to Cents using the Cents(int) constructor
const Cents objects). }

Note that there’s no confusion between the negative int main()


operator- and the minus operator- since they have a {
different number of parameters. const Cents nickle{ 5 };
std::cout << "A nickle of debt is worth " << (-nickle).getCents() << " cents\n";
Here’s another example. The ! operator is the logical
negation operator -- if an expression evaluates to “true”, return 0;
operator! will return false, and vice-versa }
The following example shows an overload of both operator- and operator! for a user-defined Point class:

#include <iostream> // Convert a Point into its negative equivalent


Point Point::operator- () const
class Point {
{ return Point(-m_x, -m_y, -m_z);
Coba 2, run code
private: }
double m_x {};
ini bahas hasilnya
double m_y {}; // Return true if the point is set at the origin, false otherwise
double m_z {}; bool Point::operator! () const
{
public: return (m_x == 0.0 && m_y == 0.0 && m_z == 0.0);
Point(double x=0.0, double y=0.0, double z=0.0): }
m_x{x}, m_y{y}, m_z{z} { } // construct = init
int main()
// Convert a Point into its negative equivalent {
Point operator- () const; Point point{}; // use default constructor to set to (0.0, 0.0, 0.0)

// Return true if the point is set at the origin if (!point)


bool operator! () const; std::cout << "point is set at the origin.\n";
else
double getX() const { return m_x; } std::cout << "point is not set at the origin.\n";
double getY() const { return m_y; }
double getZ() const { return m_z; } return 0;
}; }
#include <iostream>
Overloading the comparison operators
#include <string>
Overloading comparison operators is comparatively Coba 3, run code
class Car
simple. ini bahas hasilnya
{
private:
Because the comparison operators are all binary
std::string m_make;
operators that do not modify their left operands, we
std::string m_model;
will make our overloaded comparison operators
friend functions.
public:
Car(const std::string& make, const std::string& model)
Here’s an example Car class with an overloaded
: m_make{ make }, m_model{ model } { }
operator== and operator!=.
friend bool operator== (const Car& c1, const Car& c2);
friend bool operator!= (const Car& c1, const Car& c2);
int main() };
{
Car corolla{ "Toyota", "Corolla" }; bool operator== (const Car& c1, const Car& c2)
Car camry{ "Toyota", "Camry" }; {
return (c1.m_make == c2.m_make &&
if (corolla == camry) c1.m_model == c2.m_model);
std::cout << "a Corolla and Camry are the same.\n"; }

if (corolla != camry) bool operator!= (const Car& c1, const Car& c2)
std::cout << "a Corolla and Camry are not the same.\n"; {
return (c1.m_make != c2.m_make ||
return 0; c1.m_model != c2.m_model);
} }
Here’s a different example overloading all 6 logical comparison operators:

#include <iostream> bool operator== (const Cents& c1, const Cents& c2) int main()
{ {
class Cents Coba 4, run code return c1.m_cents == c2.m_cents; Cents dime{ 10 };
{ ini bahas hasilnya } Cents nickel{ 5 };
private:
int m_cents; bool operator!= (const Cents& c1, const Cents& c2) if (nickel > dime)
{ std::cout << "a nickel is greater than a dime.\n";
public: return c1.m_cents != c2.m_cents; if (nickel >= dime)
Cents(int cents) : m_cents{ cents } { } } std::cout << "a nickel is greater than or equal to a dime.\n";
if (nickel < dime)
friend bool operator== (const Cents& c1, const Cents& c2); bool operator< (const Cents& c1, const Cents& c2) std::cout << "a dime is greater than a nickel.\n";
friend bool operator!= (const Cents& c1, const Cents& c2); { if (nickel <= dime)
return c1.m_cents < c2.m_cents; std::cout << "a dime is greater than or equal to a nickel.\n";
friend bool operator< (const Cents& c1, const Cents& c2); } if (nickel == dime)
friend bool operator> (const Cents& c1, const Cents& c2); std::cout << "a dime is equal to a nickel.\n";
bool operator> (const Cents& c1, const Cents& c2) if (nickel != dime)
friend bool operator<= (const Cents& c1, const Cents& c2); { std::cout << "a dime is not equal to a nickel.\n";
friend bool operator>= (const Cents& c1, const Cents& c2); return c1.m_cents > c2.m_cents;
}; } return 0;
}
bool operator<= (const Cents& c1, const Cents& c2)
{
return c1.m_cents <= c2.m_cents;
}

bool operator>= (const Cents& c1, const Cents& c2)


{
return c1.m_cents >= c2.m_cents;
}
Coba 5, ganti implementasi overloading comparison coba 4 (yg kiri) dgn yg kanan, bahas perbandingannya

Minimizing comparative redundancy bool operator== (const Cents& c1, const Cents& c2) bool operator== (const Cents& c1, const Cents& c2)
{ {
In the example above, note how similar the return c1.m_cents == c2.m_cents; return c1.m_cents == c2.m_cents;
implementation of each of the overloaded comparison } }
operators are.
bool operator!= (const Cents& c1, const Cents& c2) bool operator!= (const Cents& c1, const Cents& c2)
Overloaded comparison operators tend to have a high { {
degree of redundancy, and the more complex the return c1.m_cents != c2.m_cents; return !(operator==(c1, c2)); // pakai yg sdh ada
implementation, the more redundancy there will be. } }

Fortunately, many of the comparison operators can be bool operator< (const Cents& c1, const Cents& c2) bool operator< (const Cents& c1, const Cents& c2)
implemented using the other comparison operators: { {
return c1.m_cents < c2.m_cents; return c1.m_cents < c2.m_cents;
• operator!= can be implemented as !(operator==) } }
• operator> can be implemented as operator< with the
order of the parameters flipped bool operator> (const Cents& c1, const Cents& c2) bool operator> (const Cents& c1, const Cents& c2)
• operator>= can be implemented as !(operator<) { {
• operator<= can be implemented as !(operator>) return c1.m_cents > c2.m_cents; return operator<(c2, c1); // balik argumen
} }
This means that we only need to implement logic for
operator== and operator<, and then the other four bool operator<= (const Cents& c1, const Cents& c2) bool operator<= (const Cents& c1, const Cents& c2)
comparison operators can be defined in terms of those { {
two! return c1.m_cents <= c2.m_cents; return !(operator>(c1, c2)); // operasi ! Dari (c1 > c2)
} }
Here’s an updated Cents example illustrating this:
bool operator>= (const Cents& c1, const Cents& c2) bool operator>= (const Cents& c1, const Cents& c2)
This way, if we ever need to change something, we only { {
need to update operator== and operator< instead of all return c1.m_cents >= c2.m_cents; return !(operator<(c1, c2)); // operasi ! Dari (c1 < C2)
six comparison operators! } }
#include <iostream> int main()
#include <numeric> // for std::gcd Coba 6 ??  Quiz : Add the six {
class Fraction comparison operators to the Fraction f1{ 3, 2 };
{ Fraction class so that the Fraction f2{ 5, 8 };
private: following program compiles, std::cout << f1 << ((f1 == f2) ? " == " : " not == ") << f2 <<
int m_numerator{}; dan bahas hasilnya '\n';
int m_denominator{}; std::cout << f1 << ((f1 != f2) ? " != " : " not != ") << f2 << '\n';
std::cout << f1 << ((f1 < f2) ? " < " : " not < ") << f2 << '\n';
public: std::cout << f1 << ((f1 > f2) ? " > " : " not > ") << f2 << '\n';
Fraction(int numerator = 0, int denominator = 1) : std::cout << f1 << ((f1 <= f2) ? " <= " : " not <= ") << f2 <<
m_numerator{ numerator }, m_denominator{ denominator } '\n';
{ std::cout << f1 << ((f1 >= f2) ? " >= " : " not >= ") << f2 <<
// We put reduce() in the constructor '\n';
// to ensure any new fractions we make get reduced! return 0;
// Any fractions that are overwritten will need to be re-reduced }
Coba 7  Quiz : Add an overloaded operator<< and operator< to the Car class
reduce(); at the top of the lesson so that the following program compiles, dan bahas
} hasilnya

void reduce() #include <algorithm>


{ #include <iostream>
int gcd{ std::gcd(m_numerator, m_denominator) }; #include <string>
if (gcd) #include <vector>
{
m_numerator /= gcd; int main()
m_denominator /= gcd; {
} std::vector<Car> cars{
} { "Toyota", "Corolla" },
{ "Honda", "Accord" },
friend std::ostream& operator<<(std::ostream& out, const Fraction& f1); { "Toyota", "Camry" },
}; { "Honda", "Civic" }
};
std::ostream& operator<<(std::ostream& out, const Fraction& f1)
{ std::sort(cars.begin(), cars.end()); // requires an overloaded operator<
out << f1.m_numerator << '/' << f1.m_denominator; for (const auto& car : cars) std::cout << car << '\n'; // requires an overloaded operator<<
return out; return 0;
} }
Overloading the increment and decrement operators

Overloading the increment (++) and decrement (--) operators is pretty straightforward, with one small exception. There are actually two
versions of the increment and decrement operators: a prefix increment and decrement (e.g. ++x; --y;) and a postfix increment and
decrement (e.g. x++; y--;).

Because the increment and decrement operators are both unary operators and they modify their operands, they’re best overloaded as
member functions. We’ll tackle the prefix versions first because they’re the most straightforward.

Overloading prefix increment and decrement


Digit& Digit::operator--()
Prefix increment and decrement are overloaded exactly the same as {
any normal unary operator. We’ll do this one by example: // If our number is already at 0, wrap around to 9
// otherwise just decrement to next number
#include <iostream> Coba 8  run dan bahas hasilnya
if (m_digit == 0) m_digit = 9;
class Digit else --m_digit;
{
private: int m_digit; return *this;
}
public:
Digit(int digit=0) : m_digit{digit} { } std::ostream& operator<< (std::ostream& out, const Digit& d)
{
Digit& operator++(); out << d.m_digit;
Digit& operator--(); return out;
}
friend std::ostream& operator<< (std::ostream& out, const Digit& d);
}; int main()
{
Digit& Digit::operator++() Digit digit(8);
{
// If our number is already at 9, wrap around to 0 std::cout << digit;
// otherwise just increment to next number std::cout << ++digit;
std::cout << ++digit; Note that we return *this.
if (m_digit == 9) m_digit = 0; std::cout << --digit; The overloaded increment
else ++m_digit; std::cout << --digit; and decrement operators
return the current implicit
object so multiple operators
return *this; return 0;
can be “chained” together.
} }
Overloading postfix increment and decrement Normally, functions can be overloaded when they have the same name but a
different number and/or different type of parameters. However, consider the
class Digit
{
case of the prefix and postfix increment and decrement operators. Both have
private: int m_digit; the same name (eg. operator++), are unary, and take one parameter of the
public: same type. So how it is possible to differentiate the two when overloading?
Digit(int digit=0) : m_digit{digit} { }
// int parameter means this is postfix operator++
Digit& operator++(); // prefix has no parameter Digit Digit::operator++(int) The C++ language specification
Digit& operator--(); // prefix has no parameter { has a special case that provides
Digit operator++(int); // postfix has an int parameter
// Create a temporary variable with our current digit
Digit temp{*this};
the answer: the compiler looks to
Digit operator--(int); // postfix has an int parameter Tricky (tdk lansung ++) see if the overloaded operator
// Use prefix operator to increment this digit has an int parameter.
friend std::ostream& operator<< (std::ostream& out, const Digit& d);
};
++(*this); // apply operator
If the overloaded operator has an
// return temporary result int parameter, the operator is a
// No parameter means this is prefix ++operator return temp; // return saved state postfix overload. If the
Digit& Digit::operator++()
{
}
overloaded operator has no
// If our number is already at 9, wrap around to 0 // int parameter means this is postfix operator-- parameter, the operator is a
// otherwise just increment to next number Digit Digit::operator--(int) prefix overload.
if (m_digit == 9) m_digit = 0; else ++m_digit;
{
// Create a temporary variable with our current digit
Here is the above Digit class with
Digit temp{*this}; both prefix and postfix overloads:
return *this; int main() Coba 9 run dan bahas hasilnya
} // Use prefix operator to decrement this digit {
--(*this); // apply operator Digit digit(5);
// No parameter means this is prefix --operator
Digit& Digit::operator--() // return temporary result std::cout << digit;
{ return temp; // return saved state std::cout << ++digit; // calls Digit::operator++();
// If our number is already at 0, wrap around to 9 } std::cout << digit++; // calls Digit::operator++(int);
// otherwise just decrement to next number std::cout << digit;
std::ostream& operator<< (std::ostream& out, const Digit& d) std::cout << --digit; // calls Digit::operator--();
if (m_digit == 0) m_digit = 9; else --m_digit; { std::cout << digit--; // calls Digit::operator--(int);
out << d.m_digit; std::cout << digit;
return *this; return out;
} } return 0;
}
Pembahasan :

There are a few interesting things going on here :

1. Note that we’ve distinguished the prefix from the postfix operators by providing an int dummy parameter on the postfix.
2. Because the dummy parameter is not used in the function implementation, we have not even given it a name. This tells the
compiler to treat this variable as a placeholder, which means it won’t warn us that we declared a variable but never used it.
3. Note that the prefix and postfix operators do the same job -- they both increment or decrement the object. The difference
between the two is in the value they return.

The overloaded prefix operators return the object after it has been incremented or decremented. Consequently, overloading
these is fairly straightforward. We simply increment or decrement our member variables, and then return *this.

The postfix operators, on the other hand, need to return the state of the object before it is incremented or decremented. This
leads to a bit of a conundrum -- if we increment or decrement the object, we won’t be able to return the state of the object
before it was incremented or decremented. On the other hand, if we return the state of the object before we increment or
decrement it, the increment or decrement will never be called.

The typical way this problem is solved is to use a temporary variable that holds the value of the object before it is incremented or
decremented. Then the object itself can be incremented or decremented. And finally, the temporary variable is returned to the
caller. In this way, the caller receives a copy of the object before it was incremented or decremented, but the object itself is
incremented or decremented. Note that this means the return value of the overloaded operator must be a non-reference,
because we can’t return a reference to a local variable that will be destroyed when the function exits. Also note that this means
the postfix operators are typically less efficient than the prefix operators because of the added overhead of instantiating a
temporary variable and returning by value instead of reference.

Finally, note that we’ve written the post-increment and post-decrement in such a way that it calls the pre-increment and pre-
decrement to do most of the work. This cuts down on duplicate code, and makes our class easier to modify in the future.
Overloading the subscript operator

When working with arrays, we typically use the subscript operator ([]) While this works, it’s not particularly user friendly. Consider the following
to index specific elements of an array: example:

myArray[0] = 7; // put the value 7 in the first element of the array int main()
{
However, consider the following IntList class, which has a member variable IntList list{};
that is an array: list.setItem(2, 3);

class IntList return 0;


{ }
private: int m_list[10]{};
}; Are we setting element 2 to the value 3, or element 3 to the value 2? Without
seeing the definition of setItem(), it’s simply not clear.
int main() It’s private
{ You could also just return the entire list and use operator[] to access the
IntList list{}; element:
// how do we access elements from m_list?
return 0; class IntList
} {
private:
Because the m_list member variable is private, we can not access it directly int m_list[10]{};
from variable list. This means we have no way to directly get or set values in
the m_list array. So how do we get or put elements into our list? public:
int* getList() { return m_list; }
Without operator overloading, the typical method would be to create access };
functions:
While this also works, it’s syntactically odd:
class IntList
{ int main()
private: int m_list[10]{}; {
IntList list{};
public: list.getList()[2] = 3;
void setItem(int index, int value) { m_list[index] = value; }
Coba 10  run dan bahas hasilnya
int getItem(int index) const { return m_list[index]; } return 0;
}; }
Overloading operator[] Why operator[] returns a reference Krn ada assignment
However, a better solution in this case is to overload the subscript operator
([]) to allow access to the elements of m_list. The subscript operator is one Let’s take a closer look at how list[2] = 3 evaluates. Because the subscript
of the operators that must be overloaded as a member function. An operator has a higher precedence than the assignment operator, list[2]
overloaded operator[] function will always take one parameter: the evaluates first. list[2] calls operator[], which we’ve defined to return a
subscript that the user places between the hard braces. In our IntList case, reference to list.m_list[2]. Because operator[] is returning a reference, it
we expect the user to pass in an integer index, and we’ll return an integer returns the actual list.m_list[2] array element. Our partially evaluated
value back as a result. Coba 11  run dan bahas hasilnya expression becomes list.m_list[2] = 3, which is a straightforward integer
assignment.
class IntList IntList list{};
{ list[2] = 3; // set a value In lesson 1.3 -- Introduction to objects and variables, you learned that
private: int m_list[10]{}; std::cout << list[2] << '\n'; // get a value any value on the left hand side of an assignment statement must be an l-
public: int& operator[] (int index); value (which is a variable that has an actual memory address). Because
}; return 0; the result of operator[] can be used on the left hand side of an
assignment (e.g. list[2] = 3), the return value of operator[] must be an l-
int& IntList::operator[] (int index) dua2nya value. As it turns out, references are always l-values, because you can
{ only take a reference of variables that have memory addresses. So by
return m_list[index]; returning a reference, the compiler is satisfied that we are returning an l-
} value.

Now, whenever we use the subscript operator ([]) on an object of our class, Consider what would happen if operator[] returned an integer by value
the compiler will return the corresponding element from the m_list member instead of by reference. list[2] would call operator[], which would return
variable! This allows us to both get and set values of m_list directly: the value of list.m_list[2]. For example, if m_list[2] had the value of 6,
operator[] would return the value 6. list[2] = 3 would partially evaluate to
This is both easy syntactically and from a comprehension standpoint. When 6 = 3, which makes no sense! If you try to do this, the C++ compiler will
list[2] evaluates, the compiler first checks to see if there’s an overloaded complain:
operator[] function. If so, it passes the value inside the hard braces (in this
case, 2) as an argument to the function. C:VCProjectsTest.cpp(386) : error C2106: '=' : left operand must be l-value

Note that although you can provide a default value for the function
parameter, actually using operator[] without a subscript inside is not
considered a valid syntax, so there’s no point.
#include <iostream> Coba 12  run dan bahas hasilnya
Dealing with const objects class IntList
{
In the above IntList example,
private: int m_list[10]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; // give this class some initial state for this example
operator[] is non-const, and we can
use it as an l-value to change the public:
state of non-const objects. However, int& operator[] (int index);
what if our IntList object was const? const int& operator[] (int index) const;
In this case, we wouldn’t be able to };
call the non-const version of
operator[] because that would allow
int& IntList::operator[] (int index) // for non-const objects: can be used for assignment
us to potentially change the state of
a const object. {
return m_list[index];
The good news is that we can define }
a non-const and a const version of
operator[] separately. The non-const const int& IntList::operator[] (int index) const // for const objects: can only be used for access
version will be used with non-const
{
objects, and the const version with
const-objects. return m_list[index];
}

int main()
{
IntList list{};
list[2] = 3; // okay: calls non-const version of operator[]
std::cout << list[2] << '\n';

const IntList clist{};


clist[2] = 3; // compile error: calls const version of operator[], which returns a const reference. Cannot assign to this.
If we comment out the line clist[2] = std::cout << clist[2] << '\n';
3, the above program compiles and
return 0;
executes as expected.
}
Error checking #include <cassert> // for assert()
One other advantage of overloading the subscript operator is that we can #include <array> // for std::size()
make it safer than accessing arrays directly. Normally, when accessing class IntList
arrays, the subscript operator does not check whether the index is valid. {
For example, the compiler will not complain about the following code: private: int m_list[10]{};
public: int& operator[] (int index);
int list[5]{}; };
list[7] = 3; // index 7 is out of bounds!
int& IntList::operator[] (int index)
However, if we know the size of our array, we can make our overloaded {
subscript operator check to ensure the index is within bounds: assert(index >= 0 && index < std::size(m_list));
return m_list[index];
#include <cassert> // for assert() }
#include <array> // for std::size()
Tdk perlu Coba, ketahui saja
int main()
class IntList Coba 13  run dan bahas hasilnya {
{ IntList* list{ new IntList{} };
private: int m_list[10]{}; list [2] = 3; // error: this will assume we're accessing index 2 of an array of IntLists
public: int& operator[] (int index); delete list;
}; return 0;
}
int& IntList::operator[] (int index)
{ Because we can’t assign an integer to an IntList, this won’t compile. However, if assigning
assert(index >= 0 && index < std::size(m_list)); an integer was valid, this would compile and run, with undefined results.
return m_list[index]; The proper syntax would be to dereference the pointer first (making sure to use
} parenthesis since operator[] has higher precedence than operator*), then call operator[]:

In the above example, we have used the assert() function (included in the
cassert header) to make sure our index is valid. If the expression inside int main()
the assert evaluates to false (which means the user passed in an invalid {
index), the program will terminate with an error message, which is much IntList* list{ new IntList{} };
better than the alternative (corrupting memory). This is probably the most (*list)[2] = 3; // get our IntList object, then call overloaded operator[]
common method of doing error checking of this sort. delete list;
return 0;
Pointers to objects and overloaded operator[] don’t mix }
If you try to call operator[] on a pointer to an object, C++ will assume
you’re trying to index an array of objects of that type. This is ugly & error prone. Better yet, don’t set pointers to your objects if you don’t have to.
Consider the following example: Rule : Make sure you’re not trying to call an overloaded operator[] on a pointer to an object.
The function parameter does not need to be an Overloading the parenthesis operator
integer
All of the overloaded operators you have seen so far let you define the type of
As mentioned above, C++ passes what the user types between the operator’s parameters, but not the number of parameters (which is fixed
the hard braces as an argument to the overloaded function. In based on the type of the operator). For example, operator== always takes two
most cases, this will be an integer value. However, this is not parameters, whereas operator! always takes one. The parenthesis operator
required -- and in fact, you can define that your overloaded (operator()) is a particularly interesting operator in that it allows you to vary
operator[] take a value of any type you desire. You could define both the type AND number of parameters it takes.
your overloaded operator[] to take a double, a std::string, or
whatever else you like. There are two things to keep in mind: first, the parenthesis operator must be
implemented as a member function. Second, in non-object-oriented C++, the ()
As a ridiculous example, just so you can see that it works: operator is used to call functions. In the case of classes, operator() is just a
#include <iostream> Coba 14  run dan bahas hasilnya normal operator that calls a function (named operator()) like any other
#include <string>
overloaded operator.
class Stupid
An example
{
Let’s take a look at an example that lends itself to overloading this operator:
private:
public: void operator[] (const std::string& index); class Matrix
}; {
private:
// It doesn't make sense to overload operator[] to print something double data[4][4] { };
// but it is the easiest way to show that the function parameter can be a non-integer };
void Stupid::operator[] (const std::string& index)
{ Matrices are a key component of linear algebra, and are often used to do
std::cout << index; geometric modeling and 3D computer graphics work. In this case, all you need to
} recognize is that the Matrix class is a 4 by 4 two-dimensional array of doubles.
int main() Will display Hello, world! In the lesson on overloading the subscript operator, you learned that we could
{
overload operator[] to provide direct access to a private one-dimensional array.
Stupid stupid{};
However, in this case, we want access to a private two-dimensional array. Because
stupid["Hello, world!"];
operator[] is limited to a single parameter, it is not sufficient to let us index a two-
return 0;
dimensional array.
}
However, because the () operator can take as many Now, let’s overload the () operator again, this time in a way that takes no
parameters as we want it to have, we can declare a version parameters at all:
of operator() that takes two integer index parameters, and
#include <cassert> // for assert() And here’s our new example:
use it to access our two-dimensional array. Here is an
class Matrix
example of this: Coba 15  run dan bahas hasilnya { #include <iostream>
#include <cassert> // for assert() private: double m_data[4][4] {};
class Matrix public: int main()
{ double& operator()(int row, int col); {
private: double m_data[4][4] { }; double operator()(int row, int col) const; Matrix matrix{};
public: void operator()(); matrix(1, 2) = 4.5;
double& operator()(int row, int col); }; matrix(); // erase matrix
double operator()(int row, int col) const; // for const objects std::cout << matrix(1, 2) << '\n';
}; double& Matrix::operator()(int row, int col)
{ return 0;
double& Matrix::operator()(int row, int col) assert(col >= 0 && col < 4); }
{ assert(row >= 0 && row < 4);
assert(col >= 0 && col < 4); return m_data[row][col]; which produces the result: 0
assert(row >= 0 && row < 4); }
return m_data[row][col]; Persoalan :
} double Matrix::operator()(int row, int col) const Write a class that holds a string. Overload
{ operator() to return the substring that
double Matrix::operator()(int row, int col) const assert(col >= 0 && col < 4); starts at the index of the first parameter.
{ assert(row >= 0 && row < 4); The length of the substring should be
assert(col >= 0 && col < 4); return m_data[row][col]; defined by the second parameter.
assert(row >= 0 && row < 4); } Hint: You can use array indices to access
return m_data[row][col]; individual chars within the std::string
} void Matrix::operator()() Hint: You can use operator+= to append
{ something to a string
Now we can declare a Matrix & access its elements like // reset all elements of the matrix to 0.0 The following code should run:
this: for (int row{ 0 }; row < 4; ++row)
#include <iostream> { int main() Coba 17 bahas hasilnya
int main() for (int col{ 0 }; col < 4; ++col) {
{ { Mystring string{ "Hello, world!" };
Matrix matrix; m_data[row][col] = 0.0; // start at index 7 and return 5 characters
matrix(1, 2) = 4.5; } std::cout << string(7, 5) << '\n’;
std::cout << matrix(1, 2) << '\n'; } // This should print : world
}
Coba 16 run dan bahas hasilnya return 0;
return 0;
} }
which produces the result: 4,5
In the following example, we have to use getCents() to convert our Cents variable
Overloading typecasts back into an integer so we can print it using printInt():

you learned that C++ allows you to convert one data type to another. void printInt(int value) If we have already written a lot of
The following example shows an int being converted into a double: { functions that take integers as
std::cout << value; parameters, our code will be littered with
int n{ 5 }; } calls to getCents(), which makes it more
auto d{ static_cast<double>(n) }; // int cast to a double messy than it needs to be.
int main()
C++ already knows how to convert between the built-in data types. { To make things easier, we can provide a
However, it does not know how to convert any of our user-defined Cents cents{ 7 }; user-defined conversion by overloading
classes. That’s where overloading the typecast operators comes into printInt(cents.getCents()); // print 7 the int typecast. This will allow us to cast
play. std::cout << '\n'; our Cents class directly into an int. The
return 0; following example shows how this is
User-defined conversions allow us to convert our class into another } Ini cara biasa
done:
data type. Take a look at the following class:

class Cents There are three things to note:


class Cents
{
{ 1. To overload the function that
private: int m_cents;
private: int m_cents;
public: Cents(int cents=0) : m_cents{ cents } { } casts our class to an int, we write
public: Cents(int cents=0) : m_cents{ cents } { } a new function in our class called
operator int(). Note that there is a
// Overloaded int cast
int getCents() const { return m_cents; } space between the word operator
operator int() const { return m_cents; }
and the type we are casting to.
void setCents(int cents) { m_cents = cents; }
}; int getCents() const { return m_cents; } 2. User-defined conversions do not
void setCents(int cents) { m_cents = cents; } take parameters, as there is no
}; way to pass arguments to them.
This class is pretty simple: it holds some number of cents as an integer,
and provides access functions to get and set the number of cents. It 3. User-defined conversions do not
also provides a constructor for converting an int into a Cents. Now in our example, printInt() like this:
have a return type. C++ assumes
int main()
you will be returning the correct
If we can convert an int into a Cents, then doesn’t it also make sense {
type.
for us to be able to convert a Cents back into an int? In some cases, Cents cents{ 7 };
this might not be true, but in this case, it does make sense. printInt(cents); // print 7 Kurungnya menjadi cast ke
std::cout << '\n'; int  printInt(int(cents));
return 0;
Coba 18  run dan bahas hasilnya
}
#include <iostream>
The compiler will first note that function printInt takes an integer
class Cents
parameter. Then it will note that variable cents is not an int. Finally, it will
{
look to see if we’ve provided a way to convert a Cents into an int. Since
private: int m_cents;
we have, it will call our operator int() function, which returns an int, and
public: Cents(int cents=0) : m_cents{ cents } { }
the returned int will be passed to printInt().
// Overloaded int cast
We can now also explicitly cast our Cents variable to an int:
operator int() const { return m_cents; }
Cents cents{ 7 };
int getCents() const { return m_cents; }
int c{ static_cast<int>(cents) };
void setCents(int cents) { m_cents = cents; }
};
You can provide user-defined conversions for any data type you wish,
including your own user-defined data types!
class Dollars
{
Here’s a new class called Dollars that provides an overloaded Cents
private: int m_dollars;
conversion:
public: Dollars(int dollars=0) : m_dollars{ dollars } { }
class Dollars
// Allow us to convert Dollars into Cents
{
operator Cents() const { return Cents { m_dollars * 100 }; }
private: int m_dollars;
};
public: Dollars(int dollars=0) : m_dollars{ dollars } { }
void printCents(Cents cents)
// Allow us to convert Dollars into Cents
{
operator Cents() const { return Cents{ m_dollars * 100 }; }
std::cout << cents; // cents will be implicitly cast to an int here
};
}

This allows us to convert a Dollars object directly into a Cents object! This int main()
allows you to do something like this: {
Dollars dollars{ 9 };
which makes sense, since 9 dollars is 900 cents! printCents(dollars); // dollars will be implicitly cast to a Cents here
std::cout << '\n'; Coba 19 run dan bahas hasilnya
return 0;
}

You might also like