-1

This function is supposed to take care of updating the file after calling the respective functions for depositing or withdrawing from a user account. But it fails to do just that. I've checked as much as I can and all the other functions seem to be doing their jobs properly. I've traced it down to the specific lines where the program seems to fail.

int pos = (-1) * static_cast<int>(sizeof(ac));
File.seekp(pos, ios::cur);

//File.write((char *)&ac, sizeof(BankAccount));
File.write(reinterpret_cast<char *>(&ac), sizeof(BankAccount));

Yes, these are two versions of the line because I don't fully understand how either of them work. (First version is mine, second line is from a sample program we were given)

void makeDepositOrWithdraw(int option)
{
    int num;
    cout << "\nEnter account number: ";
    cin >> num;
    int amt;
    bool found = false;
    BankAccount ac;
    SavingsAccount save;
    fstream File;
    File.open("Account.dat", ios::binary | ios::in | ios::out);
    if (!File)
    {
        cout << "Cannot retrieve database right now. Try again later";
        return;
    }
    while (!File.eof() && found == false)
    {
        File.read(reinterpret_cast<char *>(&ac), sizeof(BankAccount));
        if (ac.getAccountNo() == num)
        {
            ac.displayAcc();
            if (option == 1)
                save.makeDeposit();
            if (option == 2)
                save.makeWithdrawal();
            int pos = (-1) * static_cast<int>(sizeof(ac));
            File.seekp(pos, ios::cur);

            //File.write((char *)&ac, sizeof(BankAccount));
            File.write(reinterpret_cast<char *>(&ac), sizeof(BankAccount));

            cout << "__________________________________"
                 << "\nRecord updated ";
            found = true;
        }
    }
    File.close();
    if (!found)
        cout << "Record not found";
}

EDIT: I'm sorry for being really bad at explaining this. The initial creation of a new account works. But trying to make additional deposit doesn't run into any error, the variables are being properly updated all the way up to the main BankAccount class. But nothing gets updated in the already existing account in the .dat file. Program still executes without any error. I'm assuming if I understand the problem with the depositing part, I'll be able to solve the problem with withdrawing too, as both are handled in the same function.

#include <iostream>
#include <fstream>
#include <cctype>
#include <iomanip>
using namespace std;

class BankAccount
{
private:
    float Balance, ServChargePerMonth, AnnualInterestRt, DepoAmt, WithdrwAmt;
    int NoOfDepositPerMonth, NoOfWithdrawalPerMonth, AccountNo;
    char Name[50], type;

public:
    virtual void makeDeposit()
    {
        Balance += DepoAmt;    //adding argument to acc bal
        NoOfDepositPerMonth++; //incrementing noOfDeposit
    };

    virtual void makeWithdrawal()
    {
        Balance -= WithdrwAmt;    //subtracting argument from acc bal
        NoOfWithdrawalPerMonth++; //incrementing noOfWithdrawal
    };

    virtual void createAcc()
    {
        cout << "\nEnter your account number: ";
        cin >> AccountNo;
        cout << "\nEnter your full name: ";
        cin.ignore();
        cin.getline(Name, 50);
        cout << "\nEnter the type of Account (C for current or S for savings): ";
        cin >> type;
        type = toupper(type);
        cout << "\nEnter initial deposit amount: ";
        cin >> Balance;
        cout << "___________________________________________________________"
             << "\n\nAccount created." << endl;
    };

    void displayAcc()
    {
        cout << "\nAccount number: " << AccountNo
             << "\nAccount holder name: " << Name
             << "\nType of account: " << type
             << "\nAccount balance: $" << Balance
             << "\nTotal number of deposits: " << NoOfDepositPerMonth
             << "\nTotal number of withdrawals: " << NoOfWithdrawalPerMonth
             << "\nService Charge: $" << ServChargePerMonth << endl;
    };

    //getters
    float getBalance() { return Balance; }
    float getDepoAmt() { return DepoAmt; }
    int getNoOfDeposit() { return NoOfDepositPerMonth; }
    int getNoOfWithdraw() { return NoOfWithdrawalPerMonth; }
    int getAccountNo() { return AccountNo; }

    //setters
    void setServChargePerMonth(float servCharge) //note: increasing, not setting
    {
        ServChargePerMonth += servCharge;
    }
    void setWithdrawAmt(float Amount)
    {
        WithdrwAmt = Amount;
    }
    void setBalance(float blnce)
    {
        Balance = blnce;
    }
    void setDepoAmt(float Amount)
    {
        DepoAmt = Amount;
    }
};
class CheckingAccount : public BankAccount
{
public:
    void makeWithdrawal(float WithdrwAmt)
    {
        if ((BankAccount::getBalance() - WithdrwAmt) < 0) //note: float doens't go below 0
            setBalance(getBalance() - 15.0);              //deducting $15
        else
            BankAccount::setWithdrawAmt(WithdrwAmt);
        BankAccount::makeWithdrawal();
    }
};
class SavingsAccount : public BankAccount
{
private:
    bool statusVar;
    bool status()
    {
        if (getBalance() < 25.0)
            statusVar = false;
        else
            statusVar = true;
        return statusVar;
    }

public:
    //setter
    void setStatus(bool statusVar)
    {
        statusVar = status();
    }
    bool getStatus() { return statusVar; }

    void makeWithdrawal()
    {

        if (status()) //check if active
        {
            cout << "Enter the amount you would like to withdraw today: ";
            float WithdrwAmt;
            cin >> WithdrwAmt;
            CheckingAccount temp;
            temp.makeWithdrawal(WithdrwAmt); //perform the reqd. check, as well as call the base ver.
        }
        else
        {
            cout << "Your account has deactivated. Please increase "
                 << "your balance to reactivate your account.";
        }
    }

    void makeDeposit()
    {
        cout << "Enter the amount you would like to deposit today: ";
        float temp;
        cin >> temp;
        setDepoAmt(temp); //setting value for amt of deposit
                          //check previous balance
        if (!status())    //if <$25,
        {                 //reactivate the acc. if depoAmt brings bal to $25
            if ((getBalance() + getDepoAmt()) >= 25.0)
                setStatus(true);
        }
        //check
        BankAccount::makeDeposit(); //and then call the base ver.
    }
};

void createAcc()
{
    BankAccount ac;
    ofstream writeFile;
    writeFile.open("Account.dat", ios::binary | ios::app);
    ac.createAcc();
    writeFile.write(reinterpret_cast<char *>(&ac), sizeof(BankAccount));
    writeFile.close();
}

void makeDepositOrWithdraw(int option)
{
    int num;
    cout << "\nEnter account number: ";
    cin >> num;
    int amt;
    bool found = false;
    BankAccount ac;
    SavingsAccount save;
    fstream File;
    File.open("Account.dat", ios::binary | ios::in | ios::out);
    if (!File)
    {
        cout << "Cannot retrieve database right now. Try again later";
        return;
    }
    while (!File.eof() && found == false)
    {
        File.read(reinterpret_cast<char *>(&ac), sizeof(BankAccount));
        if (ac.getAccountNo() == num)
        {
            ac.displayAcc();
            if (option == 1)
                save.makeDeposit();
            if (option == 2)
                save.makeWithdrawal();
            int pos = (-1) * static_cast<int>(sizeof(ac));
            File.seekp(pos, ios::cur);

            //File.write((char *)&ac, sizeof(BankAccount));
            File.write(reinterpret_cast<char *>(&ac), sizeof(BankAccount));

            cout << "__________________________________"
                 << "\nRecord updated ";
            found = true;
        }
    }
    File.close();
    if (!found)
        cout << "Record not found";
}

void display()
{
    int num;
    cout << "\nEnter account number: ";
    cin >> num;
    bool flag = false;
    BankAccount ac;
    ifstream inFile;
    inFile.open("Account.dat", ios::binary);
    if (!inFile)
    {
        cout << "Cannot retrieve database right now. Try again later";
        return;
    }
    cout << "\nBALANCE DETAILS\n";
    while (inFile.read(reinterpret_cast<char *>(&ac), sizeof(BankAccount)))
    {
        if (ac.getAccountNo() == num)
        {
            ac.displayAcc();
            flag = true;
        }
    }
    inFile.close();
    if (!flag)
        cout << "\nAccount number does not exist";
}

void deleteAcc()
{
    int num;
    cout << "\nEnter account number: ";
    cin >> num;
    BankAccount ac;
    ifstream inFile;
    ofstream outFile;
    inFile.open("Account.dat", ios::binary);
    if (!inFile)
    {
        cout << "Cannot retrieve database right now. Try again later";
        return;
    }
    outFile.open("temp.dat", ios::binary);
    inFile.seekg(0, ios::beg);
    while (inFile.read(reinterpret_cast<char *>(&ac), sizeof(BankAccount)))
    {
        if (ac.getAccountNo() != num)
        {
            outFile.write(reinterpret_cast<char *>(&ac), sizeof(BankAccount));
        }
    }
    inFile.close();
    outFile.close();
    remove("Account.dat");
    rename("temp.dat", "Account.dat");
    cout << "__________________________________________"
         << "Your account has been closed.";
}

int main()
{
    char opt;
    int temp;
start:
    do
    {
        system("clear");
        cout << "\n1. Create new account"
             << "\n2. Make a deposit"
             << "\n3. Make a withdrawal"
             << "\n4. Balance enquiry"
             << "\n5. Close existing account"
             << "\n6. Exit"
             << "\n\nPlease select your option(1-6)" << endl;
        cin >> opt;
        system("clear");
        switch (opt)
        {
        case '1':
            createAcc();
            break;
        case '2':
            makeDepositOrWithdraw(1);
            break;
        case '3':
            makeDepositOrWithdraw(2);
            break;
        case '4':
            display();
            break;
        case '5':
            deleteAcc();
            break;
        case '6':
            cout << "Thank you for banking with us" << endl;
            break;
        default:
            cout << "\a" << endl;
            goto start;
            break;
        }
        cin.ignore();
        cin.get();
    } while (opt != '6');
}
  • Beside of the problem, your usage of `!File.eof()` looks [wrong](https://stackoverflow.com/questions/5431941/why-is-while-feof-file-always-wrong) and you should check if readings are successful **before** using what is read. – MikeCAT Sep 04 '20 at 11:34
  • 2
    Simply saving structures to file and loading them may not work, especially if pointers or non-trivial classes are contained in them. Please post a [Minimal, Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example). – MikeCAT Sep 04 '20 at 11:35
  • @MikeCAT I'm not sure how to do that. Am I just supposed to post the entire code? – Ritesh Rajbhandari Sep 04 '20 at 11:47
  • @RiteshRajbhandari _"I'm not sure how to do that."_ It's described fairly well in the linked article. _" Am I just supposed to post the entire code?"_ No, only the parts that are necessary to reproduce the exact error of your case. – πάντα ῥεῖ Sep 04 '20 at 11:52
  • @RiteshRajbhandari The instructions are clear enough, remove the code that isn't affecting the error you see, post a complete program that does have the error you see. In your case that would be just the I/O part. Problem is most beginners don't have the skills to transform their code like this. – john Sep 04 '20 at 11:52
  • The two versions of that line of code are completely identical, so no need to worry about that. – john Sep 04 '20 at 11:54
  • The two most important things missing from your question are, firstly, the definition of the `BankAccount` object you are trying to read/write from the file and secondly, some explanation of how you know that this code isn't working, what are you checking when you make that claim? So please update the question with this information. – john Sep 04 '20 at 11:58
  • `std::is_standard_layout::value` returns `false`, which means BankAccount cannot be used with a `File.write`. – Eljay Sep 04 '20 at 12:11

1 Answers1

0

C++ places restrictions on the kind of objects you can use binary reads and writes on. They are complicated rules but the problem with your BankAccount object is that it has virtual functions and these mean you cannot use binary reads and writes.

Another problem is that you have several different kind of objects but your code reads and writes only BankAccount objects, not CheckingAccount objects or SavingsAccount objects.

So the code as written cannot work.

It's a common beginner mistake. Binary reads and writes are very limited in their functionality but beginners try to read and write all sorts of complex objects and expect it to just work, which it doesn't.

You need to redesign how you do the I/O in your program. I'm not sure what you requirements are but dropping the use of binary I/O seems the simplest way to proceed.

john
  • 85,011
  • 4
  • 57
  • 81
  • Thank you for that info. I would've literally never thought of that otherwise – Ritesh Rajbhandari Sep 04 '20 at 12:16
  • @RiteshRajbhandari If you want to use binary I/O then your class needs to be what's called a [POD-type](https://stackoverflow.com/questions/146452/what-are-pod-types-in-c) but as I said above I think you're going to have to abandon binary I/O – john Sep 04 '20 at 12:21
  • But I can use text then, right? Is that a thing I can do? IO directly into plain text instead of binary? – Ritesh Rajbhandari Sep 04 '20 at 12:24
  • @RiteshRajbhandari • Text will work, but may require more thought as to how you want to output the text to file, and how to parse the text from file. There are many ways to do it. Whichever you choose, I recommend to keep it simple and easy. – Eljay Sep 04 '20 at 12:33
  • You're right...it's just going to be one long string of text. My immediate thought went to allocating fixed lengths to all fields and using some kind of pointer to access them later. I'll have to look into that. Thanks for the help! – Ritesh Rajbhandari Sep 04 '20 at 12:40