-1

I've written a program meant to collect the daily expenditure information from a user. I was able to get this part of the program to work. It collects data and then displays the data. To expand on the program, I now want it to store data in a text file which I can later access/modify.

Therefore I added a friend function (savedata) within the class so I could access all private data within the class. When I print the data, it seems to print the date, and the label info, but all the other information are shown as blanks. What have I done wrong and why am I not able to access the other private info within the PersonalExpenses class? If someone could help me please. Thank you.

#include <iostream>
#include <iomanip>
#include <vector>
#include <array>
#include <string>
#include <ctime>
#include <fstream>

class PersonalExpenses{
private:
static constexpr size_t expense_count{5};
static const std::array<std::string, expense_count> labels;

std::string first_name;
std::string last_name;
std::tm date;
std::array<int, expense_count> expn;

public:
PersonalExpenses();

friend void savedata(PersonalExpenses id);

friend std::istream &operator>>(std::istream &in, PersonalExpenses &ud);
friend std::ostream& operator<<(std::ostream& out, const PersonalExpenses &ud);
};

const std::array<std::string, PersonalExpenses::expense_count>   PersonalExpenses::labels {
"Medical", "Cosmetics", "Stationery", "Food & Drinks", "Assorted"
};

PersonalExpenses::PersonalExpenses() {
time_t t = time(NULL);
date = *localtime(&t);
}

std::ostream& operator<<(std::ostream& out, const PersonalExpenses &ud) {

out << ud.last_name << ", " << ud.first_name
<< "\nExpenditure for "
<< std::put_time(&ud.date, "%B %e,`%y")
<< " is \n"
<< "\n";
for (size_t i=0; i < PersonalExpenses::expense_count; ++i ) {
    out << "      " << ud.labels[i] << ":" << ud.expn[i] << "$.\n";
}

return out;
}

std::istream &operator>>(std::istream &in, PersonalExpenses &ud) {
std::string val;
std::cout << "Enter your first name:";
std::getline(std::cin,ud.first_name);
std::cout << "Enter your last name:";
std::getline(std::cin,ud.last_name);

std::cout << "How much money did you spend today?. Specify (in  Dollars) against each category."<<std::endl;
for (size_t i=0; i < PersonalExpenses::expense_count; ++i) {
    std::cout << ud.labels[i] << " = " ;
    std::getline(std::cin, val);

    if (val.empty() || val.find_first_not_of(' ') ==    std::string::npos) {
        val = "0";
    }

    ud.expn[i] = std::stoi(val);
}
std::time_t t = std::time(nullptr);
ud.date = *std::localtime(&t);
return in;
}

bool addmore() {
std::string yn;
std::cout << "Would you like to add a name to the register or quit(Press 0 to exit or 1 to continue)?";
std::getline(std::cin, yn);
return yn != "0";
}

void savedata(PersonalExpenses id)
{

    std::ofstream data_file("Expenditure_data.txt",  std::fstream::app);

    if(data_file.good())
    {
        data_file << "\n" << id.first_name << "\n";
        data_file << id.last_name << "\n";
        data_file << std::put_time(&id.date, "%B %e,`%y") << "\n";
        data_file << id.labels[0] << ":" << id.expn[0] << "$.\n";
        data_file << id.labels[1] << ":" << id.expn[1] << "$.\n";
        data_file << id.labels[2] << ":" << id.expn[2] << "$.\n";
        data_file << id.labels[3] << ":" << id.expn[3] << "$.\n";
        data_file << id.labels[4] << ":" << id.expn[4] << "$.\n";
    }
    else
    {
        //You're in trouble!
    }

 }


int main() {
std::vector<PersonalExpenses> ledger;

while (addmore()) {
    PersonalExpenses udone;
    std::cin >> udone;
    ledger.push_back(udone);
}

for (const auto &item : ledger) {
    std::cout << "\n";
    std::cout << item << "\n";
}

PersonalExpenses id;
savedata(id);

}
Big Head
  • 49
  • 6

1 Answers1

0

The reason your file contains invalid data is because you are saving invalid data. The savedata function works fine. Take a look at this part, at the end of your main :

PersonalExpenses id;
savedata(id);

This default constructs a PersonalExpenses and saves it. This reads from uninitialized members, which is undefined behavior. You function may do anything, including finishing and writing random characters to a file. Try the following example instead. It will save the first entry in the ledger, which contains valid data.

if(ledger.empty() == false) {
    savedata(ledger.front());
}
François Andrieux
  • 28,148
  • 6
  • 56
  • 87
  • Andrieux Thank you. I realize my mistake. It does work. But like you mentioned it saves the first entry. What if I want it to save multiple info entered. I'm guessing I'll have to replace ledger.front() with something else. Could you let me know what that command would be or point me in the direction of some document that will get me going. Thank you. – Big Head Jan 13 '17 at 18:35
  • @BigHead The function `void savedata(PersonalExpenses id);` only saves a single expense. You will have to change it to accept a vector of expenses like this : `void savedata(std::vector id);` and modify the body of the function to accommodate the changes. Then you can simply call ` savedata(ledger);` in your `main`. Unrelated to this, you should change `savedata` to accept const references to avoid copying everything. Ultimately, it should have this prototype : `void savedata(const std::vector & id);` – François Andrieux Jan 13 '17 at 18:41
  • Andrieux . OK. got it. thank you. I'll have to change the body of function. – Big Head Jan 13 '17 at 19:14