C++作业——用动态数组重载>>运算符

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

简介:

我有一个家庭作业要为“大于现有类型可以存储的整数”创建一个类型。 我们应该将数字存储在一个数字数组中(为了更简单的数学逻辑而向后排列)。 我正在使用动态数组将信息存储在对象中。 我需要至少重载 +、-、*、<、>、<< 和 >> 运算符。 .cpp 和 .h 文件也必须分开。

问题:

不太确定如何根据 class 属性和所需的操作重载 >> 运算符。

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

注:

此外,有时输出几乎是正确的,但包含负值或其他垃圾值。例如) cin >> 5314 , cout <<-5314

edit - 我意识到在 4 位数字之后引入了垃圾。实验继续。

赋值说明 -(对于其他上下文,这是直接 copy/paste)

C++中现有的整数类型不能存储非常大的整数。我们需要一种新类型来存储我们在处理科学问题时可能需要的这些大整数。 您可以通过将整数存储为数字数组来表示整数。

设计并实现一个 class 整数算法,其中一个数字被实现为一个数字数组。数组的每个条目都是从 0 到 9(含)的数字。 表示的数字是数组中数字的串联。

为此 class,您至少需要重载 +、-、*、<、>、<< 和 >> 运算符。尝试重载除法运算符 /.

不要忘记实现三者组合:赋值运算符、复制构造函数和析构函数。

您的除法运算符是一个整数运算符,因此它 returns 是商的整数部分。您需要了解此 class 的目的是存储大整数,因此您不应在重载这些运算符的过程中将数组表示形式转换为常规整数表示形式。同样,我们假设这些整数不能通过使用内置整数类型来处理,因此您的显式构造函数应该有一个字符串类型参数,而不是整数类型参数,并从字符串中获取每个字符,将其转换为数字并存储它到你的阵列。为了轻松地执行操作,您可能希望在数组中以相反的顺序存储一个整数。

来自老师的电子邮件说明

请回答你们中的几个问题。

  1. 整数是有符号的,因为当你做减法时你可能会得到一个负整数。所以使用数组的第一个位置来存储 0 或 1(0 表示负数,1 表示正数)。

  2. 指令不允许您将字符串参数转换为整数。我的意思是您不应该将字符串 s="123456" 转换为 int n=123456。但是,您必须将字符 1 转换为整数 1,...,将字符 6 转换为整数 6,并将每个都存储到您的数组中。

您重载的 >> 运算符似乎是正确的。 你的第二个问题:减号不是垃圾。

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

您从未将其设置为 false。我调试了你的代码,解决方法很简单:

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

我建议改用 unsigned short 类型的动态数组。 将每个数字保存在一个整数中,每个整数的范围从 -21.4 亿到 21.4 亿,这太过分了,需要大量内存。您不需要存储负值。您可以考虑使用字符,因为您可以将每个整数数字转换为 char 并向后转换,但需要更少的内存。最节省内存的方法可能是范围从 0 到 9 的枚举。

您的 class 需要存储大于 long long(8 字节)的数字至少 64 字节(number array)+ 4 字节用于 size 变量+ 1 位 (isNeg)。这对于一个数字来说是相当大的:)

根据您的任务,您不能使用整数。所以你必须改变它:) 我的方法利用了这样一个事实,即枚举 class 的每个元素都可以根据其在枚举 class 定义中的索引转换为整数类型(反之亦然)。最终,每个整数都可以转换为一个字符。因此,仍然有整数,但你很难发现它们。

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
};

您的 .cpp 文件:

#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;
}

您可能已经注意到,我用整数替换了所有 for 循环 ;) 至少我没有使用一次 int 关键字。然而,像 +10 这样的东西当然是一个常量整数。

到目前为止,我还不能确定反向存储数字是否有利。

乘法由你决定。干得漂亮:)

这个重载解决了垃圾值的问题。

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;
}