简体   繁体   中英

C++ Homework - overloading >> operator with dynamic array

Intro:

I have a homework assignment to create a type for "ints larger than existing types can store." We should store the numbers in an array of digits(backward for easier math logic). I'm using a dynamic array to store the info in the object. I am required to overload at least the +, -, *, <, >, << and >> operators. Also.cpp and.h files must be separate.

Problem:

Not too sure how to overload the >> operator based on the class attributes and manipulation needed.

BigIntegers.h
#include <string>
#include <iostream>

typedef int* BigIntPtr;

class BigIntegers {
private:
    int size; // based on string size, if neg string size -1
    BigIntPtr number; // dynamic array ptr
    bool isNeg; // set default to false, assumes a positive number
public:
    explicit BigIntegers(std::string num = "");
    BigIntegers(const BigIntegers &bi);
    ~BigIntegers();

    friend std::istream &operator>>(std::istream &is, BigIntegers &bi) {
        /**
         * using eg "is >> bi.data;" doesn't seem viable given the data manipulation needed
         * see constructor
         */
        std::string input;
        getline(is,input);
        bi = BigIntegers(input);
        return is;
    }

    friend std::ostream &operator<<(std::ostream &os, const BigIntegers &bi) {
        if(bi.isNeg) //add sign if needed
            os << '-';
        for(int s=bi.size-1;s>-1;s--) //print reverse
        {
            os << bi.number[s];
        }
        return os;
    }
};
BigIntegers.cpp
#include <algorithm>
#include "BigIntegers.h"

BigIntegers::BigIntegers(std::string num) {
    //if null
    if(num.empty())
    {
        size = 0;
        number = NULL;
        isNeg = 0;
        return;
    }

    //determine if its negative
    if (num.find('-') == 0)
    {
        num.erase(remove(num.begin(),num.end(), '-'),num.end());
        isNeg =true;
    }else {isNeg= false;}
    size = num.length();
    number = new int[size];
    //add array backwards for math optimization
    std::string rev; rev.assign(num.rbegin(),num.rend());
    for(int i = 0; i < size; i++)
    {
        number[i]=rev[i]-'0';
    }
}

BigIntegers::~BigIntegers() {
    delete [] number;
    size =0;
    isNeg =0;
}
#include <iostream>
#include "BigIntegers.h"

using std::cout;
using std::cin;
using std::string;

int main() {
    //basic functionality test
    string stringInt = "123456";
    string stringIntNeg = "-99987654321";

    BigIntegers test1(stringInt);
    cout << test1 << "\n";
    BigIntegers test2(stringIntNeg);
    cout << test2 << "\n";

    //iostream test
    cout << "Enter a big integer in the form 123456 or -123456.\n";
    BigIntegers test3;
    cin >> test3;
    cout << test3 << "\n";

    return 0;
}
output
pr4_bigIntegers\cmake-build-debug\pr4_bigIntegers.exe
123456
-12345678987654321
Enter a big integer in the form 123456 or -123456.
5789256
-57883070081-2144186072

Process finished with exit code 0

Note:

Additionally, sometimes the output is almost correct but negative or some other garbage values are included. eg) cin >> 5314, cout <<-5314

edit - I've realized that after 4 digits the garbage is introduced. The experimentation continues.

Assignment Instructions - (for additional context, this is a direct copy/paste)

The existing types of integers in C++ cannot store very large integers. We need a new type that can store these large integers we possibly need in dealing with scientific problems. You can represent an integer by storing the integer as an array of digits.

Design and implement a class for integer arithmetic in which a number is implemented as an array of digits. Each entry of the array will be a digit from 0 to 9 (inclusive). The number represented is the concatenation of the digits in the array.

You are required to overload at least the +, -, *, <, >, << and >> operators for this class. Try to overload the division operator /.

Do not forget to implement the gang of three: assignment operator, copy constructor and destructor.

Your division operator is an integer operator so that it returns the integer part of the quotient. You need to understand that the purpose of this class is to store large integers so you should not convert your array representation into regular integer representation during the process of overloading these operators. Again, we assume that these integers cannot be handled by using the build-in integer types so your explicit constructor should have a string type parameter, not an integer type parameter, and get each character from the string, convert it to a digit and store it to your array. To perform operations easily, you may want to store an integer in reversed order in your array.

  • Use dynamic array to store your integer.
  • Include professional documentation of your code and proper indentation
  • Separate your header file from implementation file
  • Test every aspect of your class.

Email clarification from teacher

Just answer a couple of questions from some of you.

  1. The integers are signed because when you do your subtraction you may get a negative integer. So use the first spot of the array to store 0 or 1 (0 for negative and 1 for positive).

  2. The instructions do not allow you to convert the string parameter to an integer. I mean that you should not convert string s="123456" to int n=123456. But, you have to convert character 1 into integer 1, ..., character 6 into integer 6 and store each into your array.

Your overloaded >> operator seems to be correct. Your second problem: The minus sign is no garbage.

bool isNeg; // set default to false, assumes a positive number

You never set it to false. I debugged your code, and the solution is simple:

BigIntegers::BigIntegers(std::string num) : isNeg(false) {
    //your constructor stuff
}

I suggest using a dynamic array of type unsigned short instead. Saving each digit in an integer each ranging from -2.14 Billion to 2.14 Billion is overkill will require a lot of memory. You don't need negative values to store. You may consider using chars, as you can convert each integer digit into a char and backward but less memory is required. The most memory-efficient way is perhaps an Enum ranging from 0 to 9.

Your class would require storing a number greater than a long long (8 bytes) at least 64 bytes ( number array ) + 4 bytes for the size variable + 1 bit ( isNeg ). This is quite large for a number:)

According to your task, you are not allowed to use integers. So you have to change it:) My approach exploits the fact that each element of an enum class can be converted to an integer type according to its index in the enum class definition (and vice versa). Eventually, each integer can be converted to a char. Thus, there are still integers but you can spot them hardly.

enum class Digit{Zero,One,Two,Three,Four,Five,Six,Seven,Eight,Nine};

typedef Digit* BigIntPtr;

class BigIntegers 
{
public:
    explicit BigIntegers(std::string num = "");
    BigIntegers(const BigIntegers& src);
    BigIntegers& operator=(const BigIntegers&src);
    ~BigIntegers();

    friend bool operator<(const BigIntegers& lhs, const BigIntegers& rhs);
    friend bool operator>(const BigIntegers& lhs, const BigIntegers& rhs);
    friend BigIntegers operator+(const BigIntegers& lhs, const BigIntegers& rhs);
    friend BigIntegers operator-(const BigIntegers& lhs, const BigIntegers&rhs);

    friend std::istream &operator>>(std::istream &is, BigIntegers &bi);
    friend std::ostream &operator<<(std::ostream &os, const BigIntegers &bi);
private:
    size_t size; // based on string size, if neg string size -1
    BigIntPtr number; // dynamic array ptr
    bool isNeg; // set default to false, assumes a positive number
};

Your.cpp file:

#include <algorithm>
#include "bigint.h"

using namespace std;

BigIntegers::BigIntegers(std::string num):isNeg(false)
{
    //if null
    if (num.empty())
    {
        size = 0;
        number = NULL;
        isNeg = 0;
        return;
    }


    //determine if its negative
    if (num.find('-') == 0)
    {
        num.erase(remove(num.begin(), num.end(), '-'), num.end());
        isNeg = true;
    }
    size = num.length();
    number = new Digit[size];

    //add array backwards for math optimization
    std::string rev; rev.assign(num.rbegin(), num.rend());
    Digit * aux = number;
    std::for_each (rev.begin(),rev.end(),[&](const char c)
    {
        *number = Digit(c - '0');
        number++;
    });

    number = aux;
}

BigIntegers::BigIntegers(const BigIntegers & src)
    : size(src.size), number{new Digit[src.size]}, isNeg(src.isNeg)
{
    for (auto i = number, j = src.number; i < number + size; i++, j++)
    {
        *i = *j;
    }
}

BigIntegers & BigIntegers::operator=(const BigIntegers & src)
{
    if (this == &src)
        return *this;

    size = src.size;
    isNeg = src.isNeg;

    if (number != NULL) delete[] number;
    number = new Digit[src.size];
    for (auto i = number, j = src.number; i < number + size; i++,j++)
    {
        *i = *j;
    }

    return *this;
}

BigIntegers::~BigIntegers()
{
    delete[] number;
}


bool operator<(const BigIntegers & lhs, const BigIntegers & rhs)
{
    if (lhs.size > rhs.size) return false;
    if (lhs.size < rhs.size) return true;

    for (auto i = lhs.number + lhs.size - 1, j = rhs.number + rhs.size - 1; i >= lhs.number || j >= rhs.number; i--, j--)
    {
        if (char(*i) > char(*j))return false;
        if (char(*i) < char(*j))return true;
    }

    return false;
}

bool operator>(const BigIntegers & lhs, const BigIntegers & rhs)
{
    return !(lhs < rhs);
}

BigIntegers operator+(const BigIntegers & lhs, const BigIntegers & rhs)
{
    string value = "";
    Digit aux = Digit::Zero;
    for (auto i = lhs.number, j = rhs.number; i < lhs.number + lhs.size || j < rhs.number + rhs.size; i++, j++)
    {
        char c = char(aux);
        c += i < lhs.number + lhs.size ? char(*i) : char(0);
        c += j < rhs.number + rhs.size ? char(*j) : char(0);
        aux = Digit(0);
        if (c > 9)
        {
            aux = Digit::One;
            c -= 10;
        }
        // 48 is '0' in Ascii table
        value += (c+48);
    }

    if (aux == Digit::One)
    {
        value += '1';
    }

    reverse(value.begin(), value.end());

    return BigIntegers(value);
}

BigIntegers operator-(const BigIntegers & lhs, const BigIntegers & rhs)
{
    bool reverse = false;
    if (lhs < rhs)reverse = true;
    const BigIntegers& bigger = reverse ? rhs : lhs;
    const BigIntegers& smaller = reverse ? lhs : rhs;

    Digit aux = Digit::Zero;
    std::string value = "";
    for (auto i = bigger.number, j = smaller.number; i < bigger.number+bigger.size; i++, j++)   
    {
        char c1 = char(*i);
        char c2 = j < smaller.number+smaller.size ? char(*j) : 0;
        c2 += char(aux);
        aux = Digit::Zero;
        if (c1 < c2)
        {
            aux = Digit::One;
            c1 = c1 + 10 - c2;
        }
        else
        {
            c1 -= c2;
        }

        if (c1 > 0 || i < bigger.number + bigger.size - 1)
        {
            // if condition is to avoid leading zeros
            value += (c1 + 48);
        }
    }

    if (reverse)value += "-";
    std::reverse(value.begin(), value.end());

    return BigIntegers(value);
}

istream& operator>>(istream& is, BigIntegers& bi)
{
    std::string input;
    getline(is, input);
    bi = BigIntegers(input);
    return is;
}

std::ostream &operator<<(std::ostream &os, const BigIntegers &bi) {
    if (bi.isNeg) //add sign if needed
        os << '-';
    for (int s = bi.size - 1; s > -1; s--) //print reverse
    {
        os << static_cast<int>(bi.number[s]);
    }
    return os;
}

As you may notice, I replaced all for loops using an integer;) At least I am not using the int keyword once. However, something like +10 is of course a const integer.

Up to this point, I have not yet been able to determine that storing the digits in reverse is advantageous.

The multiplication is up to you. Nice task:)

This overload is what solved the problem of the garbage values.

BigIntegers &BigIntegers::operator=(const BigIntegers &bi) {
    //if number is already assigned here
    if(this==&bi) 
        return *this;
    //else assign the number
    size = bi.size; isNeg = bi.isNeg;
    if (number != nullptr) delete[] number;
    number= new int[bi.size];
    for (auto i = number, j = bi.number; i < number+size; i++, j++)
        *i = *j;
    return *this;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM