Week 8

Classes

The following header file, rational.h, declares a class that represents a rational number:

#ifndef RATIONAL_H
#define RATIONAL_H

class Rational {
    private:
        int numerator;
        int denominator;

    public:
        // Constructs a rational number with value 0
        Rational();

        // Constructs a rational number with value n
        Rational(int n);

        // Constructs a rational number with value a/b
        Rational(int a, int b);

        // Inverts the rational number
        void invert();

        // Returns the value of the rational number as a real number
        double value() const;

        // Multiplies two rational numbers
        Rational operator*(Rational) const;

        // Divides two rational numbers
        Rational operator/(Rational) const;
};

#endif

The member variables numerator and denominator represent the numerator and denominator of the rational number, respectively.

This class could be used in an application file as follows.

#include <iostream>
#include <vector>
#include "rational.h"

using namespace std;

int main() {
    Rational x(3, 4);
    Rational y(1, 2);

    cout << "x.value(): " << x.value() << '\n';
    cout << "y.value(): " << y.value() << '\n';

    y.invert();
    cout << "y.value(): " << y.value() << '\n';

    Rational z = x * y;
    cout << "z.value(): " << z.value() << '\n';

    z = x / y;
    cout << "z.value(): " << z.value() << '\n';

    Rational w(3);
    cout << "w.value(): " << w.value() << '\n';

    vector<Rational> v = {Rational(), Rational(1), Rational(-4, -2), w};
    for (const Rational& q : v) {
        cout << q.value() << ' ';
    }
    cout << '\n';

    return 0;
}

The expected output would then be

x.value(): 0.75
y.value(): 0.5
y.value(): 2
z.value(): 1.5
z.value(): 0.375
w.value(): 3
0 1 2 3 

Exercise 8.1

In the following exercise, assume that the implementations are contained in a separate file rational.cpp (in the same directory as rational.h) prefaced by

#include "rational.h"

(a) Implement the constructors Rational::Rational according to their specifications above. Refer to the source code of the application file and its output for examples of their usage.

Solution

Here is one possible solution.

Rational::Rational() {
    numerator = 0;
    denominator = 1;
}

Rational::Rational(int n) {
    numerator = n;
    denominator = 1;
}

Rational::Rational(int a, int b) {
    numerator = a;
    denominator = b;
}

Alternative solution

Here is another possible solution, using member initializer lists.

Rational::Rational() : numerator {0}, denominator {1} {}

Rational::Rational(int n) : numerator {n}, denominator {1} {}

Rational::Rational(int a, int b) : numerator {a}, denominator {b} {}


(b) Implement the member function Rational::invert according to its specification above.

Solution

Here is one possible solution.

void Rational::invert() {
    int temp = numerator;
    numerator = denominator;
    denominator = temp;
}


(c) Implement the member function Rational::value according to its specification above.

Solution

Here is one possible solution.

double Rational::value() const {
    double numerator_d = numerator;
    double denominator_d = denominator;

    return numerator_d / denominator_d;
}


(d) Overload the operator * according to the specification above. Note that the right operand will be passed as the argument.

Solution

Here is one possible solution.

Rational Rational::operator*(Rational other) const {
    return Rational(numerator * other.numerator,
                    denominator * other.denominator);
}


(e) Overload the operator / according to the specification above. Note that the right operand will be passed as the argument.

Solution

Here is one possible solution.

Rational Rational::operator/(Rational other) const {
    return Rational(numerator * other.denominator,
                    denominator * other.numerator);
}

Alternative solution

Here is another possible solution, assuming that Rational::invert and Rational::operator* have been defined as in parts (b) and (d).

Rational Rational::operator/(Rational other) const {
    other.invert();
    return *this * other;
}

Pointers

Exercise 8.2

(a) What is the output of the following program?

#include <iostream>

using namespace std;

int main() {
    int i = 10;
    int j = 20;

    int* ptr = &i;
    ptr = &j;
    j = 30;

    cout << "i: " << i << '\n';
    cout << "j: " << j << '\n';
    cout << "*ptr: " << *ptr << '\n';

    return 0;
}
Solution

i: 10
j: 30
*ptr: 30

Notice that the statement ptr = &j makes ptr point to j.


(b) What is the output of the following program?

#include <iostream>

using namespace std;

int main() {
    int i = 10;
    int j = 20;

    int& ref = i;
    ref = j;
    j = 30;

    cout << "i: " << i << '\n';
    cout << "j: " << j << '\n';
    cout << "ref: " << ref << '\n';

    return 0;
}
Solution

i: 20
j: 30
ref: 20

Notice that the statement ref = j does not make ref refer to j; it only copies the value of j to what is referred to by ref (namely, i). As ref still refers to i, it is unaffected by the statement j = 30.

Arrays

See Section 5.3 (Pointers and Arrays) in The C Programming Language. (The rest of the chapter is also an excellent reference!)