overloading operators issue with addition

2020-05-09 18:12发布

问题:

Having issues with adding objects. It seems to work when adding two, ex: 2.34 + 34.57 = 36.81, but fails when adding 3 or more ex: 6.78 + 9.81 + 4.59 = 79.59 <-- the total seems to increase drastically for some reason.

The converter functions can be ignored since they simply translate the numerical total to english format and are working. The median and sorting, and > functions work as well, so the issue might be somewhere in the ostream, istream, or + functions. I used cout a whole bunch of places and most of the abnormal numbers seems to appear in the + function.

Thanks in advance.

Header:

#ifndef LAB2_H
#define LAB2_H 
#include <iostream>
#include <string>
#include <cstdlib>

using namespace std;

class DollarAmount
{
    double dollar = 0.00;
    double cents = 0.00;
    double avg = 0.00;
    int maxsequence = 0;
    string number;
    public:

        DollarAmount(double num1 = 0, double num2 = 0);

        double getavg() const { return avg; };

        double getDollar() const { return dollar; };

        double getCent() const { return cents; };

        int getmaxsequence() const { return maxsequence; };

        friend istream& operator >> (istream& ins, DollarAmount& arg);

        friend ostream& operator << (ostream& out, const DollarAmount& arg);

        friend DollarAmount operator + (const DollarAmount& arg1, const DollarAmount& arg2);

        friend bool operator > (const DollarAmount& arg1, const DollarAmount& arg2);

        void sortobjects(DollarAmount a[], int length);

        void median(DollarAmount a[], int length);

        //All of the below functions are from lab 1

        void converter1(string array1[], string array2[], string array3[], double m);

        void converter2(double n);

Functions:

#include <iostream>
#include <string>
#include <cstdlib>
#include "lab2.h"

using namespace std;


DollarAmount::DollarAmount(double num1, double num2)
{

    dollar = num1;
    cents = num2;

}

istream& operator >> (istream& ins, DollarAmount& arg)
{
    double num1 = 0.00;
    double num2 = 0.00;
    //int num1 = 0;
    //int num2 = 0;
    char ch;
    string s;
    double num3;
    bool wrongInput = false;

   //ins >> num1 >> ch >> num2;
    ins >> num3;
    s = to_string(num3);
    arg.number = s;
    int index = s.find(".");
    num1 = atof(s.substr(0, index).c_str());
    num2 = atof(s.substr(index, 3).c_str()); 

    do
    {
        if (wrongInput == true)
        {
            cout << "Enter the expenditure record (e.g., $1.95, coffee, enter -1 to end):$";
            ins >> num1 >> ch >> num2;
        }

        else if (cin.fail() || num1 < 0 || num1 > 9999 || num2 > 99) //in case the above fails, e.g., ten dollor five cents...
        {
            cout << "Wrong input types. Try again:\n";
            cin.clear(); //clear the error flags in cin 
            cin.ignore(2048, '\n'); //ignore everthing in the input buffer, up to 2048 char, 
            //up to the newline char =< ignore the rest of the line
            wrongInput = true;
        }
        else
            wrongInput = false;
    } while (wrongInput == true || num1 < 0 || num1 > 9999 || num2 > 99); // if input false ask for input again

    arg.dollar = num1;
    arg.cents = num2;
    arg.maxsequence = arg.getmaxsequence() + 1;
    return ins;
}

ostream& operator << (ostream& out, const DollarAmount& arg)
{
    out << arg.getDollar() + arg.getCent();
    return out;
}

DollarAmount operator + (const DollarAmount& arg1, const DollarAmount& arg2)
{
    DollarAmount temp;
    double sum = 0.00;
    double x = 0.00;
    double y = 0.00;
    string z;

    temp.dollar = 0.00;
    temp.cents = 0.00;

    x = arg1.dollar + arg1.cents;

    y = arg2.dollar + arg2.cents;
    sum = x + y;
    cout << "This is the sum: " << sum << endl;

    z = to_string(sum);
    int index = z.find(".");

    temp.dollar = atof(z.substr(0, index).c_str());
    temp.cents = atof(z.substr(index + 1, 2).c_str());
    cout << "This is the dollar: " << temp.getDollar() << endl;
    cout << "This is the cents: " << temp.getCent() << endl;

    return temp;
}

bool operator > (const DollarAmount& arg1, const DollarAmount& arg2)
{
    DollarAmount temp;
    double x, y;

    x = arg1.dollar + arg1.cents;

    y = arg2.dollar + arg2.cents;

    if (x > y)
        return true;
    else
        return false;
}

void DollarAmount::sortobjects(DollarAmount a[], int length)
{
    int i;
    int last = length - 1;  //point to the index of last element in unsorted part of the array 

    for (int pass = 0; pass < length; pass++)
    {
        //a[0]...a[last] is not sorted 
        //repeated bubble the largest element in this range to the end ... 
        for (i = 0; i <= last - 1; i++)
        {
            if (a[i] > a[i + 1])
            {
                //swap 
                DollarAmount tmp;
                tmp = a[i + 1];
                a[i + 1] = a[i];
                a[i] = tmp;
            }
        }
        //at this point, the largest element in a[0...last] is stored in a[last]

        //unsorted part is now a[0...last-1]
        last = last - 1;
    }
}


void DollarAmount::median(DollarAmount a[], int length)
{
    DollarAmount temp;
    int mid;
    double average = 0.00;
    double x = 0.00;
    double y = 0.00;

    if (length % 2 == 0)
    {
        mid = length / 2;
        int test = mid - 1;
        x = a[mid].dollar + a[mid].cents;
        y = a[test].dollar + a[test].cents;
        average = (x + y) / 2.00;
        temp.avg = average;
        cout << temp.getavg();
    }
    else
    {
        mid = length / 2;
        x = a[mid].dollar + a[mid].cents;
        average = x;
        temp.avg = average;
        cout << temp.getavg();
    }

}

void DollarAmount::converter1(string array1[], string array2[], string array3[], double m)
{
    string a;
    string b;
    string c;
    string d;
    int num1 = 0;

    d = to_string(m);
    int index = d.find(".");
    int x = atoi(d.substr(0, index).c_str());

    if (x < 100)
    {

        if (x > 0 && x < 10)
        {
            a = array1[x - 1];
            cout << a;
        }

        else if (x == 10)
        {
            a = array2[0];
            cout << a;
        }

        else if (x > 10 && x < 20)
        {
            a = array2[x - 10];
            cout << a;
        }

        else if (x == 20)
        {
            a = array3[0];
            cout << a;
        }

        else if (x > 20 && x < 30)
        {
            num1 = x - 20;
            b = array3[0];
            a = array1[num1 - 1];
            cout << b << " " << a;
        }

        else if (x == 30)
        {
            a = array3[1];
            cout << a;
        }

        else if (x > 30 && x < 40)
        {
            num1 = x - 30;
            b = array3[1];
            a = array1[num1 - 1];
            cout << b << " " << a;
        }

        else if (x == 40)
        {
            a = array3[2];
            cout << a;
        }

        else if (x > 40 && x < 50)
        {
            num1 = x - 40;
            b = array3[2];
            a = array1[num1 - 1];
            cout << b << " " << a;
        }

        else if (x == 50)
        {
            a = array3[3];
            cout << a;
        }

        else if (x > 50 && x < 60)
        {
            num1 = x - 50;
            b = array3[3];
            a = array1[num1 - 1];
            cout << b << " " << a;
        }

        else if (x == 60)
        {
            a = array3[4];
            cout << a;
        }

        else if (x > 60 && x < 70)
        {
            num1 = x - 60;
            b = array3[4];
            a = array1[num1 - 1];
            cout << b << " " << a;
        }

        else if (x == 70)
        {
            a = array3[5];
            cout << a;
        }

        else if (x > 70 && x < 80)
        {
            num1 = x - 70;
            b = array3[5];
            a = array1[num1 - 1];
            cout << b << " " << a;
        }

        else if (x == 80)
        {
            a = array3[6];
            cout << a;
        }

        else if (x > 80 && x < 90)
        {
            num1 = x - 80;
            b = array3[6];
            a = array1[num1 - 1];
            cout << b << " " << a;
        }

        else if (x == 90)
        {
            a = array3[7];
            cout << a;
        }

        else
        {
            num1 = x - 90;
            b = array3[7];
            a = array1[num1 - 1];
            cout << b << " " << a;
        }
    }

    else if (x < 1000)
    {
        if (x == 100)
        {
            a = array1[0] + " hundred ";
            cout << a;
        }

        else if (x == 200)
        {
            a = array1[1] + " hundred ";
            cout << a;
        }

        else if (x == 300)
        {
            a = array1[2] + " hundred ";
            cout << a;
        }

        else if (x == 400)
        {
            a = array1[3] + " hundred ";
            cout << a;
        }

        else if (x == 500)
        {
            a = array1[4] + " hundred ";
            cout << a;
        }

        else if (x == 600)
        {
            a = array1[5] + " hundred ";
            cout << a;
        }

        else if (x == 700)
        {
            a = array1[6] + " hundred ";
            cout << a;
        }

        else if (x == 800)
        {
            a = array1[7] + " hundred ";
            cout << a;
        }

        else if (x == 900)
        {
            a = array1[8] + " hundred ";
            cout << a;
        }

        else
        {
            // a + "hundred + b + c
            num1 = x / 100;
            a = array1[num1 - 1];
            cout << a << " hundred ";
            converter1(array1, array2, array3, (x % 100));
        }
    }

    else
    {
        if (x == 1000)
        {
            a = array1[0] + " thousand";
            cout << a;
        }

        else if (x == 2000)
        {
            a = array1[1] + " thousand";
            cout << a;
        }

        else if (x == 3000)
        {
            a = array1[2] + " thousand";
            cout << a;
        }

        else if (x == 4000)
        {
            a = array1[3] + " thousand";
            cout << a;
        }

        else if (x == 5000)
        {
            a = array1[4] + " thousand";
            cout << a;
        }

        else if (x == 6000)
        {
            a = array1[5] + " thousand";
            cout << a;
        }

        else if (x == 7000)
        {
            a = array1[6] + " thousand";
            cout << a;
        }

        else if (x == 8000)
        {
            a = array1[7] + " thousand";
            cout << a;
        }

        else if (x == 9000)
        {
            a = array1[8] + " thousand";
            cout << a;
        }

        else
        {
            //a + "thousand" + b + "hundred" + c + d
            num1 = x / 1000;
            a = array1[num1 - 1];
            cout << a << " thousand ";
            converter1(array1, array2, array3, (x % 1000));
        }
    }
}


void DollarAmount::converter2(double n)
{
    cout << n << "/100)" << endl;

}

Main:

#include <iostream>
#include <string>
#include <cstdlib>
#include "lab2.h"

using namespace std;

const int INIT_SIZE = 10;
const int a = 9;
const int b = 10;
const int c = 8;

int main()
{
    string one[a] = { "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" };
    string two[b] = { "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" };
    string three[c] = { "twenty", "thirty", "fourty", "fifty", "sixty", "seventy", "eighty", "ninety" };

    DollarAmount* arr = new DollarAmount[INIT_SIZE];
    DollarAmount* ptr = NULL;
    int avg = 0;
    int arr_size = INIT_SIZE;
    int arr_len = 0; // actual number of objects stored in it , also the index
      // of next free slot in arr

    bool lastInput = false;

    do 
    {
        if (arr_len == arr_size)
        { // the array is full, need to grow! 

           //allocate an array double the cur_size
            ptr = new DollarAmount[arr_size * 2];

            //copy each element in arr into the new array that ptr points to... 
            // Todo: please figure out how to do this...(hint: use a for loop)
            for (int i = 0; i < arr_size; i++)
            {
                ptr[i] = arr[i];
            }

            //now we delete the current arr
            delete[] arr;
            arr = ptr;
            arr_size = arr_size * 2;
        }

        cout << "Enter the expenditure record (e.g., $1.95, coffee, enter 0.0 to end):$";
        cin >> arr[arr_len];  //read in a dollar amount from cin we will allow input such as 0.0

        //If the last read dollar amount is 0.0, then it's the end 
        if (arr[arr_len].getDollar() == 0 && arr[arr_len].getCent() == 0)
            lastInput = true;
        else //we only increment arr_len for input that's not 0.0
            arr_len++;


    } while (lastInput == false);

    DollarAmount total, temp, loopingtotal;


    total = arr[0] + arr[1];
    //cout << "The first total: " << total << endl;
    //cout << "This is sum of first two: " << total;

    //A loopt to add all DollarAmount up, and display the total 

    for (int i = 2; i < arr_len; i++)
    {
        total = total + arr[i];
        cout << "The total for this " << i << " time is: " << total << endl;
    }
    //cout << "This is the sum after loop " << total;

    //Call Sort function to sort the array of DollarAmount 
    temp.sortobjects(arr, arr_len);
    //Display the sorted array, and the median 
    cout << "Sorted list of expenditure:" << endl;
    for (int i = 0; i < arr_len; i++)
    {
        cout << arr[i] << endl;
    }

    //cout << "This is the total cents: " << total.getCent();
    //cout << "This is the total dollar: " << total.getDollar();
    cout << "The total is $" << total.getDollar() << "." << total.getCent();
    cout << " (";
    temp.converter1(one, two, three, total.getDollar());
    cout << " and "; 
    temp.converter2(total.getCent());
    cout << "The median is ";
    temp.median(arr, arr_len);
    cout << "." << endl;
    cout << "Bye!";
    return 0;
}

回答1:

TL;DR - Keep your class as simple as possible to adequately represent one thing well. Input validation can be a rabbit hole, so just worry about your current requirements. Do all your math outside the class (because it's unrelated).

You're working harder than you need to be. First, your class is a mess. A class should have a singular purpose, and it's clear to me that yours does not.

Start with a Money class:

class Money {
public:
    Money() = default;
    Money(int d, int c)
        : cents_m(d * 100 + c)
    {
    }
    inline int dollars() const { return cents_m / 100; }
    inline int cents() const { return cents_m % 100; }
    friend std::istream& operator>>(std::istream& sin, Money& obj);
    friend std::ostream& operator<<(std::ostream& sout, const Money& obj);
    friend const Money operator+(const Money& lhs, const Money& rhs);

private:
    int cents_m = 0;
};

Leave out all that extra math garbage, it doesn't belong in a Money class. It belongs wherever you need that kind of work done as functions. Note that instead of separate dollars and cents, you are now storing only cents. $5 is 500 pennies, and this makes all simple Money math a lot easier. If you were dealing with interest rates and stock trades, then you'd care about fractions of pennies, but I'm guessing you don't care here.

I'm taking advantage of some C++11 features like Default Member Initialization and defaulting the default constructor now that cents_m is default initialized for me. I'm also using the initialization section, and so should you.

To showcase how much easier Money math just got, here's the operator+() function:

const Money operator+(const Money& lhs, const Money& rhs)
{
    int sum = lhs.cents_m + rhs.cents_m;

    return Money(sum / 100, sum % 100);
}

Here are the other operators:

std::istream& operator>>(std::istream& sin, Money& obj)
{
    std::string tmp;
    std::getline(sin, tmp);
    if (!(tmp.length() > 0)) {
        obj = Money();
        return sin;
    }

    if (tmp[0] == '$') {
        tmp = tmp.substr(1);
    }

    std::size_t location;
    double value = std::stod(tmp, &location);
    if (location != tmp.length()) {
        obj = Money();
    } else {
        int cents = std::lround(value * 100);
        obj = Money(cents / 100, cents % 100);
    }

    return sin;
}

std::ostream& operator<<(std::ostream& sout, const Money& obj)
{
    bool isNegative = obj.cents_m < 0;
    std::string tmp;
    int dollars = abs(obj.cents_m / 100);
    int cents = abs(obj.cents_m % 100);

    if (isNegative) {
        tmp += "(";
    }
    tmp += ("$" + std::to_string(dollars) + ".");
    if (cents < 10) {
        tmp += ("0" + std::to_string(cents));
    } else {
        tmp += std::to_string(obj.cents_m % 100);
    }
    if (isNegative) {
        tmp += ")";
    }

    return sout << tmp;
}

NOTES: The input stream operator overload can account for a '$' being entered (but only as the first character), but it likely cannot account for a negative amount of Money (since it would have to be typed $-3.50, and no one does that). Input validation is not simple and how far down that rabbit hole you go depends on what your teacher requires. I will admit that my code will readily break under some invalid inputs because I didn't feel like getting very comprehensive (and we're still only talking about ASCII).

I also chose to simply make the Money object store default values on the few invalid inputs I detected. An error message with loops in the operator overload is a bad idea (That's not how these operators behave for anything else; overloads should behave as expected). Either handle the error in the background or throw an exception. This is a case-by-case decision, and I think that silently handling the issue is find in this situation.

If you are allowed to assume a valid number is always entered, that operator overload gets very simple.

The stream output operator overload can at least print negative Money.

Here's a short main function to test the Money class:

int main()
{
    Money one(6, 78);
    Money two(9, 81);
    Money thr(4, 59);

    std::cout << (one + two + thr) << '\n';

    Money fou;
    std::cout << "Give me some money: ";
    std::cin >> fou;
    std::cout << "You only gave me " << fou << "?\n";
}

Some general advice: the first thing you should do when approaching a programming problem is rarely write code. Think on it, visualize it, write out requirements in your own words, and think on how you would solve it (not with code, the actual steps you would need to take). The code is a tool, not the solution.