1
\$\begingroup\$

Question:

Need to create three classes.

  1. Number
  2. Fraction
  3. Integer

Requirements

  1. The first class should support “display”, “==”, and “+”.

  2. "Display" : This operation displays the Number itself in its original form. "+" : This operation adds the number itself with another number and returns a third number whose numeric value is equal to the sum of the numeric values of the former two.

  3. The Fraction class, which is represented by its numerator and denominator (both are integers).

  4. For a Fraction, it needs to be displayed in its original form. That is, 2/4 has to be displayed as “2/4”, not “1/2” or “0.5”.

Solution

Number.h

#pragma once

template<class T>
class Number
{
    virtual T& operator= (const T &) = 0;      
    virtual const T operator+ (const T &) = 0;
    virtual void display() = 0;
};

Fraction.h

#pragma once
#include "Number.h"

class Fraction : public Number<Fraction>
{

private:
    int _numerator;
    int _denominator;
public:
    void display();
    Fraction();
    Fraction(int num, int den);
    Fraction& operator= (const Fraction &); 
    const Fraction operator+ (const Fraction &); 
    int Fraction::gcdCalculate(int val1, int val2);
    int Fraction::lcmCalculate(const int val1, const int val2);
    ~Fraction();

};

Fraction.cpp

#include "Fraction.h"
#include <iostream>

using namespace std;

// constructor
Fraction::Fraction()
{
    _numerator = 0;
    _denominator = 1;
}

// parameterised constructor setting the denominator and numerator values
Fraction::Fraction(int num, int den)
{
    _numerator = num;
    _denominator = den;
}

// display the fraction value
void Fraction::display()
{
    std::cout << this->_numerator << "/";   
    std::cout << this->_denominator << endl;
}

// "=" operator overloading
Fraction& Fraction::operator=(const Fraction &num)
{
    _numerator = num._numerator;
    _denominator = num._denominator;
    return *this;
}

// "+" operator overloading
const Fraction Fraction::operator+(const Fraction &numberTwo)
{
    Fraction outputFraction;
    int lcm = lcmCalculate(this->_denominator, numberTwo._denominator);
    int multiplier1 = 0;
    if (this->_denominator)
        multiplier1 = lcm / this->_denominator;
    int multiplier2 = 0;
    if (numberTwo._denominator)
        multiplier2 = lcm / numberTwo._denominator;
    outputFraction._numerator = this->_numerator * multiplier1 + numberTwo._numerator * multiplier2;
    outputFraction._denominator = lcm;
    return outputFraction;
}

// LCM Calculation
int Fraction::lcmCalculate(const int val1, const int val2)
{
    int temp = gcdCalculate(val1, val2);
    return temp ? (val1 / temp * val2) : 0;
}

// GCD Calculation
int Fraction::gcdCalculate(int val1, int val2)
{
    for (;;)
    {
        if (val1 == 0) return val2;
        val2 %= val1;
        if (val2 == 0) return val1;
        val1 %= val2;
    }
}

// destructor
Fraction::~Fraction()
{}

Integer.h

#pragma once
#include "Number.h"

class Integer : public Number<Integer>
{

private:
    int intValue;
public:
    void display();
    Integer();
    Integer(int num);
    Integer& operator= (const Integer &);
    const Integer operator+ (const Integer &); 
    ~Integer();

};

Integer.cpp

#include "Integer.h"
#include "Number.h"
#include <iostream>

using namespace std;

// constructor
Integer::Integer()
{
    intValue = 0;
}

// parameterized constructor
Integer::Integer(int num)
{
    intValue = num;
}

// display the int value
void Integer::display()
{
    std::cout << this->intValue << endl;     
}

// operator "=" overloading 
Integer& Integer::operator=(const Integer &secondNumber)
{
    intValue = secondNumber.intValue;
    return *this;
}

// operator "+" overloading
const Integer Integer::operator+(const Integer &secondNumber)
{
     Integer temp;
     temp.intValue = this->intValue + secondNumber.intValue;
     return temp;
}

// destructor
Integer::~Integer(){}

Main.cpp

#include "Integer.h"
#include <iostream>
#include "Fraction.h"
using namespace std;

int main()
{

    Integer sumOfInteger;
    Integer int1(30);
    Integer int2(40);
    sumOfInteger = int1 + int2;
    sumOfInteger.display();
    Fraction sumOfFraction;
    Fraction fracOne(2,4);
    Fraction fracTwo(2,4);
    sumOfFraction = fracOne + fracTwo;
    sumOfFraction.display();
    return 0;

}

Can someone please review and help me in improving my overall coding skills and standards.

\$\endgroup\$
4
  • \$\begingroup\$ Your title says "Add fraction and integer", but nowhere do you have code for adding a fraction to an integer. Does the title mean "Add fractions and add integers" rather than "add fractions to integers"? Either way, it's confusing. \$\endgroup\$ Mar 27, 2016 at 15:56
  • \$\begingroup\$ correcting it.. \$\endgroup\$ Mar 27, 2016 at 16:01
  • \$\begingroup\$ Sorry about the confusing title. Hope its okey now. \$\endgroup\$ Mar 27, 2016 at 16:02
  • \$\begingroup\$ void Fraction::display() missing nominator print (e.g. std::cout << _numerator;) \$\endgroup\$
    – Nick
    Mar 29, 2016 at 7:06

2 Answers 2

4
\$\begingroup\$

It looks like you are trying to use the Curiously-Recurring Template Pattern to achieve static polymorphism, however you also use the virtual keyword. virtual is used for runtime polymorphism, hence choose one paradigm, CRTP, e.g., static or virtual, e.g, runtime.

You put your variables at the top of the class, and used the private access specifier. Classes are already private by default, so the private specifier is superfluous.

Your display() function is hard to unit-test because it causes side effects. Declare display to take a stream as an input parameter. Then you can unit-test the function with a string stream, and pass std::cout when you want to print stuff out.

template <typename OStream>
void display(OStream& os) { os << "Write your data here."; }

Your destructors are default, so declare them as such:

class Fraction {
    ~Fraction() = default;
}

const Fraction Fraction::operator+(const Fraction &numberTwo) returns a copy as const. Never return const since it prevents the Named Value Return Optimization (NVRO). Additionally, this function doesn't mutate its members, so make it member const:

Fraction Fraction::operator+(const Fraction &numberTwo) const

More Explanation

Number.h

#pragma once

template<class T>
class Number
{
public:
    T& operator= (const T &)
    {
        return impl().operator=(T);
    }

    T operator+ (const T &) const
    {
        return impl().operator+(T);
    }

    template <typename Stream>
    void display(Stream& os) const
    {
        impl().display(os);
    }

private:
    T& impl() {
        return *static_cast<T*>(this);
    }
    T const & impl() const {
        return *static_cast<T const *>(this);
    }

};

Fraction.h

#pragma once
#include "Number.h"

class Fraction : public Number<Fraction>
{
    int _numerator{0};
    int _denominator{1};
public:
    template <typename Stream>
    void display(Stream& os) const
    {
        os << this->_numerator << "/";   
        os << this->_denominator << '\n';
    }

    Fraction() = default;
    Fraction(int num, int den);

    ~Fraction() = default;
    
    Fraction& operator= (const Fraction &);
    Fraction operator+ (const Fraction &) const; 

    int Fraction::gcdCalculate(int val1, int val2) const;
    int Fraction::lcmCalculate(const int val1, const int val2) const;

};

Fraction.cpp

#include "Fraction.h"
#include <iostream>

using namespace std;

// parametrised constructor setting the denominator and numerator values
Fraction::Fraction(int num, int den)
    :
    _numerator(num),
    _denominator(den)
{
}

// "=" operator overloading
Fraction& Fraction::operator=(const Fraction &num)
{
    _numerator = num._numerator;
    _denominator = num._denominator;
    return *this;
}

// "+" operator overloading
Fraction Fraction::operator+(const Fraction &numberTwo) const
{
    Fraction outputFraction;
    int lcm = lcmCalculate(this->_denominator, numberTwo._denominator);
    int multiplier1 = 0;
    if (this->_denominator)
        multiplier1 = lcm / this->_denominator;
    int multiplier2 = 0;
    if (numberTwo._denominator)
        multiplier2 = lcm / numberTwo._denominator;
    outputFraction._numerator = this->_numerator * multiplier1 + numberTwo._numerator * multiplier2;
    outputFraction._denominator = lcm;
    return outputFraction;
}

// LCM Calculation
int Fraction::lcmCalculate(const int val1, const int val2) const
{
    int temp = gcdCalculate(val1, val2);
    return temp ? (val1 / temp * val2) : 0;
}

// GCD Calculation
int Fraction::gcdCalculate(int val1, int val2) const
{
    for (;;)
    {
        if (val1 == 0) return val2;
        val2 %= val1;
        if (val2 == 0) return val1;
        val1 %= val2;
    }
}

Integer.h

#pragma once
#include "Number.h"

class Integer : public Number<Integer>
{
    int intValue{0};
public:
    template <typename Stream>
    void display(Stream& os) const
    {
        os << this->intValue << '\n';     
    }

    Integer() = default;
    ~Integer() = default;

    Integer(int num);
    Integer& operator= (const Integer &);
    Integer operator+ (const Integer &) const; 

};

Integer.cpp

#include "Integer.h"
#include "Number.h"
#include <iostream>

using namespace std;

// parameterized constructor
Integer::Integer(int num)
    :
    intValue(num) //Prefer to use initializer lists
{
}

// operator "=" overloading 
Integer& Integer::operator=(const Integer &secondNumber)
{
    intValue = secondNumber.intValue;
    return *this;
}

// operator "+" overloading
Integer Integer::operator+(const Integer &secondNumber) const
{
    Integer temp;
    temp.intValue = this->intValue + secondNumber.intValue;
    return temp;
}

Main.cpp

#include "Integer.h"
#include <iostream>
#include "Fraction.h"
using namespace std;


template <typename INumberType>
void GenericDisplay(const Number<INumberType>& num) //Here we are calling through the Number<> Interface
{
    num.display(std::cout);
}


int main()
{

    //Use the Classes Directly
    /// --> Don't declare stuff until you need it... Integer sumOfInteger;
    Integer int1(30);
    Integer int2(40);
    auto sumOfInteger = int1 + int2;
    GenericDisplay(sumOfInteger);

    Fraction fracOne(2,4);
    Fraction fracTwo(2,4);
    auto sumOfFraction = fracOne + fracTwo;
    GenericDisplay(sumOfFraction);

    return 0;

}
\$\endgroup\$
3
  • \$\begingroup\$ I had to use template because I could not overload the operators + as it returns class instance. And we cannot instantiate abstract class. that's why I used template. As far as virtual goes I explicitly wanted my child class to implement these methods so I applied virtual key word and also did "=0 " to make the function pure virtual. Can you please guide me how to proceed. Thanks \$\endgroup\$ Mar 28, 2016 at 0:17
  • \$\begingroup\$ Also applying const keyword to this =>>> raction Fraction::operator+(const Fraction &numberTwo) const throws red squiggly. \$\endgroup\$ Mar 28, 2016 at 0:28
  • 1
    \$\begingroup\$ Why use a template parameter OStream instead of just taking a reference to std::ostream? If you want to support different character types, maybe template<typename CharT> void display(std::basic_ostream<CharT>& os) {...}? It is important to restrict the type here, because << might work for things which are not a stream, so it will compile but do the wrong thing. \$\endgroup\$
    – G. Sliepen
    Jul 22, 2022 at 9:24
-1
\$\begingroup\$

When it compiles using version 20, there is an error:

error: 'T' does not refer to a value
        return impl().operator=(T);

It could be because we are using a "." operator or some other pointer issue.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Welcome to Code Review! The question was tagged with c++11. Are you able to compile for that version or one prior to version 20? \$\endgroup\$ Jul 23, 2022 at 6:27

Not the answer you're looking for? Browse other questions tagged or ask your own question.