0
\$\begingroup\$

So I wrote this a while ago and yesterday I was going through it and trying to remember every line of code and how it works. Well, the program simulates a online bank account as well as an ATM. You register your account, you are able to withdraw, deposit, and transfer money. There is a limit to the amount you can transfer and withdraw in a day, there is a limit number for attempts, if you exceed it you will be locked out of the account for 24 hours, you can see all the transactions you've made, and there is a log for the admin, and all of that is saved to file.

Anyway, it is a pretty long program (thus the feeling that the design is wrong), what I am asking is not to go by line by line and review it, just to check out how the program works and review its design and give some feedback. However, if you want to review the code itself or if you something that is not right I would appreciate your feedback on that.

Paths.h

#ifndef PATHS_H_INCLUDED
#define PATHS_H_INCLUDED

class Paths
{
public:
    static const std::string BASE_PATH;

    static const std::string USER_PATH;
    static const std::string ADMIN_PATH;

    static const std::string USER_INFO;
    static const std::string USER_TRANSACTION;

    static const std::string ADMIN_BACKUP;
    static const std::string ADMIN_LOCKED_ACCOUNTS;
    static const std::string ADMIN_LOG;

    static const std::string ADMIN_WITHDRAWAL_DATE;
    static const std::string ADMIN_WITHDRAWAL_VAL;
    static const std::string ADMIN_TRANSFER_DATE;
    static const std::string ADMIN_TRANSFER_VAL;

};


#endif // PATHS_H_INCLUDED

Paths.cpp

#include <iostream>

#include "Paths.h"

using namespace std;

const string Paths::BASE_PATH = "C:\\ProgramData\\BankAcount";

const string Paths::USER_PATH = BASE_PATH + "\\User";
const string Paths::ADMIN_PATH = BASE_PATH + "\\Administrator";

const string Paths::USER_INFO = USER_PATH + "\\Info\\";
const string Paths::USER_TRANSACTION = USER_PATH + "\\Transactions\\";


const string Paths::ADMIN_BACKUP = ADMIN_PATH + "\\Backup\\";
const string Paths::ADMIN_LOCKED_ACCOUNTS = ADMIN_PATH + "\\LockedAccounts\\";
const string Paths::ADMIN_LOG = ADMIN_PATH + "\\Log\\";

const string Paths::ADMIN_TRANSFER_DATE = ADMIN_PATH + "\\Limits\\Transfer\\Dates\\";
const string Paths::ADMIN_TRANSFER_VAL = ADMIN_PATH + "\\Limits\\Transfer\\Values\\";
const string Paths::ADMIN_WITHDRAWAL_DATE = ADMIN_PATH + "\\Limits\\Withdrawal\\Dates\\";
const string Paths::ADMIN_WITHDRAWAL_VAL = ADMIN_PATH + "\\Limits\\Withdrawal\\Values\\";

classAccount.h

#ifndef CLASSACCOUNT_H_INCLUDED
#define CLASSACCOUNT_H_INCLUDE

class Account
{
protected:
    std::string name;
    std::string new_name;
    long long int balance;
    std::string account_num;
    std::string file_path;

    std::string pin, new_pin;

    char buffer[256];

    void createFolder() const;

    void withdrawal();
    void deposit ();
    void viewBalance() const;
    void transfer();
    void logTransactions(const std::string& log_account_num, const std::string& transaction_type, const int& amount) const;
    void transactions(const std::string& account_num) const;
    void log( bool note ) const;
    void logInsideAccount(const std::string& in_account) const;

    void withdrawalLimit(int withdrawalAmount);
    void transferLImit (int transferAmount);

    void createLimitFiles();
    void dayShiftCheck();

    void deleteFile(std::string file_to_delete);

    void settings();

    void changePin();
    void pinChanged();
    void changeName();
    void nameChanged();
    void clearTransactions();
    void transactionsCleared();

    // int numberOfTransactions = 0;

    void lockAccount();
    void lockAccountCheck();

    void signUp();
    void signIn();
    void myAccount(std::ifstream* fileStream);
    void signOut ();

    void parseAccountInfo(const char* buffer, std::string& assigned_name, std::string& assigned_account_num, long long int& assigned_Balance, std::string& assigned_pin) const;

    void backupAccountFile(const std::string& file) const;
    void backupTransactionFile(const std::string& file) const;


    std::string accountExistenceCheckSignIn(std::string& account_num);
    std::string accountExistenceCheckSignUp(std::string& account_num) ;
    std::string accountExistenceCheckTransfer(std::string& account_num) ;

public:

    void mainMenu();
};


#endif

classAccount.cpp

#include <iostream>
#include <fstream>
#include <iomanip>
#include <sstream>
#include <cstring>
#include <stdlib.h>
#include <conio.h>
#include <direct.h>
#include <windows.h>
// #include <algorithm>
// #include <list>

#include <sys/types.h>
#include <sys/stat.h>

#include "classAccount.h"
#include "Paths.h" // a class where constant strings, that contains entire paths to where file containing the necessary info, will be managed

using namespace std;

// a function that creates the directories where the account files  will be storaged
// it checks wether the necessary folders are already created before creating the folders
void Account::createFolder() const
{
    class stat info; // used when checking the existence of the folder

    if(stat(Paths::BASE_PATH.c_str(), &info) != 0)          // I could ommit this path and it still would be created in the next lines but I chose to write
        system(("mkdir " + Paths::BASE_PATH).c_str());  //  it so the code can be slightly more readable, at least in my opinion

    if(stat(Paths::USER_PATH.c_str(), &info) != 0)
        system(("mkdir " + Paths::USER_PATH).c_str() );

    if(stat(Paths::USER_INFO.c_str(), &info) != 0)
        system(("mkdir " + Paths::USER_INFO).c_str() );

    if(stat(Paths::USER_TRANSACTION.c_str(), &info) != 0)
        system(("mkdir " + Paths::USER_TRANSACTION).c_str() );

    if(stat(Paths::ADMIN_PATH.c_str(), &info) != 0)        // I could ommit this path and it still would be created in the next lines but I chose to write
        system(("mkdir " + Paths::ADMIN_PATH).c_str() );  //  so the code can be slightly more readable, at least in my opinion

    if(stat(Paths::ADMIN_BACKUP.c_str(), &info) != 0)
        system(("mkdir " + Paths::ADMIN_BACKUP).c_str() );

    if(stat(Paths::ADMIN_LOCKED_ACCOUNTS.c_str(), &info) != 0)
        system(("mkdir " + Paths::ADMIN_LOCKED_ACCOUNTS).c_str() );

    if(stat(Paths::ADMIN_LOG.c_str(), &info) != 0)
        system(("mkdir " + Paths::ADMIN_LOG).c_str() );

    //  paths to where the dates and values information will be saved for withdrawal and transfer limits purposes

    if(stat(Paths::ADMIN_WITHDRAWAL_DATE.c_str(), &info) != 0)
        system(("mkdir " + Paths::ADMIN_WITHDRAWAL_DATE).c_str() );

    if(stat(Paths::ADMIN_WITHDRAWAL_VAL.c_str(), &info) != 0)
        system(("mkdir " + Paths::ADMIN_WITHDRAWAL_VAL).c_str() );

    if(stat(Paths::ADMIN_TRANSFER_DATE.c_str(), &info) != 0)
        system(("mkdir " + Paths::ADMIN_TRANSFER_DATE).c_str() );

    if(stat(Paths::ADMIN_TRANSFER_VAL.c_str(), &info) != 0)
        system(("mkdir " + Paths::ADMIN_TRANSFER_VAL).c_str() );
}

void Account::deleteFile(std::string file_to_delete)
{
    // convert string to const char* so it can be used in file_path for deletion
    /*
      const char* deleteFile = sfile_path.data();
      const char* deleteFile = sfile_path.c_str();
      const char* delete_file = &file_to_delete[0];
    */
    remove(file_to_delete.c_str());
}

// display the simple menu at the beginning
void Account::mainMenu()
{
    system("cls");

    createFolder();

    //  mkdir("D:\Bank Account");

    account_num.clear();
    pin.clear();

    char option;

    cout << "\n\n\n\n\n\n \t\t\t\t\t" << "1 -> Sign In" << endl;
    cout << " \t\t\t\t\t" << "2 -> Sign Up" << endl << endl;
    cout << " \t\t\t\t\t" << " -> ";
    option = _getch();

    while (option != '1' && option != '2' && option != 27)
    {
        option = _getch();
    }

    switch (option)
    {
    case '1':
        system("cls");
        signIn();
        break;

    case '2':
        system("cls");
        signUp();
        break;

    case '0':
        cout << "\nProcess returned 0" << endl << "Press enter key to continue";
        cin.get();
        exit(0);
        break;

    case 27:
        cout << "\nProcess returned 0" << endl << "Press enter key to continue";
        cin.get();
        exit(0);
        break;

    default:
        mainMenu();
        break;

    }
}


// a function to lock the account that had 3 failed attempts on a password input
void Account::lockAccount()
{
    string sfile_path = Paths::ADMIN_LOCKED_ACCOUNTS + account_num;

    time_t now = time(nullptr);
    string time_now = ctime(&now); // takes the system times into a string, showing the lock time
    tm* lock_time = localtime(&now);

    ofstream lock (sfile_path); // create the file that contains the time of the lock

    lock << lock_time->tm_mday << " " <<  lock_time->tm_hour << " " <<  lock_time->tm_min;

    lock.close();
}

// a function that everytime a user goes to sign in it checks if the account is locked or not
void Account::lockAccountCheck()
{
    string sfile_path = Paths::ADMIN_LOCKED_ACCOUNTS + account_num;

    ifstream file_in (sfile_path);

    // checks for the existence of a file that contains the time of the lock, if the file doesn't exist it returns meaning that
    // the account is not locked
    if ( ! file_in.good() )
    {
        return;
    }

    time_t now = time(nullptr);
    string time_now = ctime(&now);
    tm* lock_time = localtime(&now);

    char bufferLine[256];

    // integers that hold the dates values
    int filelock_timeDay;
    int filelock_timeHour;
    int filelock_timeMinute;

    int lock_timeDay = lock_time->tm_mday;
    int lock_timeHour = lock_time->tm_hour;
    int lock_timeMinute = lock_time->tm_min;

    // it gets the information of the time from the lock account file and hold it in char
    file_in.getline(bufferLine, 256);

    istringstream lockFile (bufferLine); // it parses the contents of the file

    // it assigns each date value to the respective variable
    lockFile >> filelock_timeDay;
    lockFile >> filelock_timeHour;
    lockFile >> filelock_timeMinute;

    //  count the days, hours and minutes to check if the time of the lock has passed
    // it compares the time of the system against the lock time
    int count_day = lock_timeDay - filelock_timeDay;
    int count_hour = lock_timeHour - filelock_timeHour;
    int count_minute = lock_timeMinute - filelock_timeMinute;

    file_in.close();

    if ( count_day == 0 )
    {
        cout << endl << endl << "Your Account Has Been Locked Since " << filelock_timeDay << " - " << 1 + lock_time->tm_mon
             << " - " << 1900 + lock_time->tm_year << " | " << filelock_timeHour << ":" << filelock_timeMinute << endl << endl;

        cout << "Locking Period: 24 Hours" ;

        cout  << endl<< endl << "Press enter key to continue";

        char ch;

        ch = _getch();

        while (ch != 13)
        {
            ch = _getch();
        }

        mainMenu();

    }
    else if (count_day > 0 && count_hour < 0 )
    {
        cout << endl << endl  << "Your Account Has Been Locked Since " << filelock_timeDay << " - " << 1 + lock_time->tm_mon
             << " - " << 1900 + lock_time->tm_year << " | " << filelock_timeHour << ":" << filelock_timeMinute << endl << endl;

        cout << "Locking Period: 24 Hours" ;

        cout  << endl<< endl << "Press enter key to continue";

        char ch;

        ch = _getch();

        while (ch != 13)
        {
            ch = _getch();
        }

        mainMenu();
    }
    /*
    else if (count_day > 0 && filelock_timeHour < lock_timeHour)
    {

    }
    */
    else if (count_day > 0 && count_hour >= 0 && lock_timeHour == filelock_timeHour && count_minute < 0 )
    {
        cout << endl << endl  << "Your Account has Been Locked Since " << filelock_timeDay << " - " << 1 + lock_time->tm_mon
             << " - " << 1900 + lock_time->tm_year << " | " << filelock_timeHour << ":" << filelock_timeMinute << endl << endl;

        cout << "Locking Period: 24 Hours" ;

        cout  << endl<< endl << "Press enter jey to continue";

        char ch;

        ch = _getch();

        while (ch != 13)
        {
            ch = _getch();
        }

        mainMenu();
    }

    deleteFile(sfile_path); // the file that contains the  locking info will be deleted after it is known that it has passed 24hours after the lock
}

// a function that creates a file that contains log information
void Account::log(bool note ) const // the bool note is a bool that  logs wether the account was locked because of too many failed password attempts
{
    time_t now = time(nullptr);
    // string time_now = ctime(&now);
    tm* log_time = localtime(&now);

    int log_day, log_month, log_year;
    string sLog_day, sLog_month, sLog_year;

    log_day = log_time->tm_mday;
    log_month = 1 + log_time->tm_mon;
    log_year = 1900 + log_time->tm_year;

    // the dates obtained and held in integer variables will be coverted to string so they can be added to the file path
    sLog_day = to_string(log_day);
    sLog_month = to_string(log_month);
    sLog_year = to_string(log_year);

    string sfile_path = Paths::ADMIN_LOG + sLog_day + " - " + sLog_month + " - " + sLog_year;

    ifstream file_in(sfile_path);

    string existent_content;
    char c;

    while (file_in.good() && ! file_in.eof())
    {
        c = file_in.get();
        existent_content.push_back(c);
    }

    ofstream log(sfile_path);

    log << existent_content << endl << endl;
    log << "-----------------------------------------------------------------------------------" << endl << endl;
    log << "Account Number: " << account_num << " | " << "Name: " << name << endl;
    log << "Sign In " << " at " << log_time->tm_hour << ":" << log_time->tm_min << ":" << log_time->tm_sec;
    //log << "Sign In " << " at " << __DATE__;

    // if the note is true it logs in the file that the account was blocked for 24 hours and informing the time of the occured
    if (note == true)
    {
        log << endl << "Account Locked For 24 Hours Started At " << log_time->tm_hour << ":" << log_time->tm_min << ":" << log_time->tm_sec;
    }

    log.close();
}

// a function that creates a file  that logs every activity inside an account
void Account::logInsideAccount(const string& inAccount_activity) const // the string will take the type of activity made
{
    time_t now = time(nullptr);
    // string time_now = ctime(&now);
    tm* log_time = localtime(&now);

    int log_day, log_month, log_year;
    string sLog_day, sLog_month, sLog_year;

    log_day = log_time->tm_mday;
    log_month = 1 + log_time->tm_mon;
    log_year = 1900 + log_time->tm_year;

    sLog_day = to_string(log_day);
    sLog_month = to_string(log_month);
    sLog_year = to_string(log_year);

    string sfile_path = Paths::ADMIN_LOG + sLog_day + " - " + sLog_month + " - " + sLog_year;

    ifstream file_in(sfile_path);

    string existent_content;
    char c;

    // while the log file is ok and hasn't reached the end it will keep copying the content to the string existent_content
    while (file_in.good() && ! file_in.eof())
    {
        c = file_in.get();
        existent_content.push_back(c);
    }

    ofstream log(sfile_path);
    log << existent_content << endl;
    log << " - " << inAccount_activity << " at " << log_time->tm_hour << ":" << log_time->tm_min << ":" << log_time->tm_sec ;
    // log << " - " << inAccount_activity << " at " << __TIME__ ;

    log.close();  // closes the file after use
}

// log every transaction
void Account::logTransactions(const string& log_account_num, const string& transaction_type, const int& amount) const
{
    time_t now = time(nullptr);

    char* time_now = ctime(&now);

    string sfile_path = Paths::USER_TRANSACTION + log_account_num;

    ifstream file_in(sfile_path); // it enters the file

    string existent_content;
    char c;

    // copies the exitent content in the transaction file to a string
    while (file_in.good() && ! file_in.eof())
    {
        c = file_in.get();
        existent_content.push_back(c);
    }

    file_in.close(); // closes the file after use

    /* should've used ios::app for appending thus eliminating the need to write the previous
     code to save the existing content of the file */
    ofstream fileOut (sfile_path); // creates  the file with the same name and path of the previous file, thus overwriting

    /* Desnecessary */
    fileOut << existent_content << endl << endl; // it writes the already logged transactions to the file and below it writes the recent transaction to the file
    /* Desnecessary */

    fileOut << "-> " << time_now;// << endl ;
    fileOut << "[ " << transaction_type << " ] -> ";
    fileOut << "Amount: " << amount;

    fileOut.close(); // closes the file after use

    backupTransactionFile(log_account_num);
}

void Account::parseAccountInfo(const char* buffer, string& assigned_name, string& assigned_account_num,
                               long long int& assigned_balance, string& assigned_pin) const
{
    // associate an istrstream object with the accountut
    // character string
    istringstream account(buffer);
    account >> assigned_name;

    // now the account number
    account >> assigned_account_num;

    // and the balance
    account >> assigned_balance;

    account >> assigned_pin;
}

// a function that creates a backup file of the account file and of the transaction
void Account::backupAccountFile(const string& file) const
{
    string sfile_path = Paths::USER_INFO + file; // original file path

    string source(sfile_path);
    string target = Paths::ADMIN_BACKUP + file; // backup file path

    ifstream input (source.c_str(), ios_base::in | ios_base::binary);
    ofstream output (target.c_str(), ios_base::out | ios_base::binary | ios_base::trunc);

    if (input.good() && output.good())
    {
        while( ! input.eof() && input.good())
        {
            char buffer[4096];

            input.read(buffer, 4096);
            output.write(buffer, input.gcount());
        }
    }
}

void Account::backupTransactionFile(const string& file) const
{
    string sfile_path = Paths::USER_TRANSACTION + file;

    string source(sfile_path);
    string target = Paths::ADMIN_BACKUP + file + "-trans" ;

    ifstream input (source.c_str(), ios_base::in | ios_base::binary);
    ofstream output (target.c_str(), ios_base::in | ios_base::binary | ios_base::trunc);

    if (input.good() && output.good())
    {
        while ( ! input.eof() && input.good())
        {
            char buffer[4096];

            input.read(buffer, 4096);
            output.write(buffer, input.gcount());
        }
    }
}

// function that checks wether the file exists or not when the user is signing in
string Account::accountExistenceCheckSignIn(string& account_num) // the string takes the account number input by the user
{
    string sfile_path = Paths::USER_INFO + account_num;

    char ch_num;
    int num_length = 1;

    ifstream file_stream(sfile_path);

    // a condition that checks wether the file exists or not, if it is good it means that the file exists thus the account too
    if (file_stream.good())
    {
        return sfile_path;
    }

    account_num.clear();

    while (true)
    {
        cout << endl << endl << "Account Doesn't Exist!!" << endl;
        cout << endl << "Enter Your Bank Account Number: ";
        ch_num = getch();

        // if the user wishes to go back to the menu he/she presses esc
        if (ch_num == 27)
        {
            mainMenu();
        }

        while (ch_num == 13 && num_length <= 16)
        {
            if (account_num.empty())
                cout << "\b  \b";

            ch_num = getch();

            // if the user wishes to go back to the menu he/she presses esc
            if (ch_num == 27)
            {
                mainMenu();
            }
        }

        // while the user doesnt hit enter, 13 is the ASCII code for enter
        while (ch_num != 13)
        {
            if (account_num.empty())
            {
                cout << "  \b";
            }

            // when the user hits backspace, 8 s the ASCII code for backspace
            if (ch_num == 8)
            {
                if ( ! account_num.empty())
                {
                    -- num_length;
                    account_num.pop_back();
                }

                cout << "\b \b";
            }
            // if the pin lenght is equal or below 4 asterisk will be displayed to mask the pin and account_num will be take the character input by the user
            // only numbers are accepted
            else if (num_length <= 16 && isdigit(ch_num))
            {
                if (account_num.empty())
                    cout << "\b \b";

                account_num.push_back(ch_num);

                cout << ch_num;

                ++num_length;
            }
            else if ( account_num.empty()) // when the user clear the input this condition won't allow the cursor to improper forward move
            {
                cout << "\b \b";
            }

            ch_num = _getch();

            // if the user wishes to go back to the menu he/she presses esc
            if (ch_num == 27)
            {
                mainMenu();
            }

            while (ch_num == 13 && num_length <= 16)
            {
                if (account_num.empty())
                {
                    cout << " \b";

                }

                ch_num = _getch();

                // if the user wishes to go back to the menu he/she presses esc
                if (ch_num == 27)
                {
                    mainMenu();
                }
            }
        }

        // when the user learns that the account doesnt exist he or she has the optionion of entering '0' and go back to the main menu
        if (account_num[0] == '0' && account_num.size() == 1)
        {
            mainMenu();
        }

        sfile_path = Paths::USER_INFO + account_num;

        ifstream file_stream (sfile_path);

        // the user will keep being noticed that the account doesnt exist until he or she either inputs a valid account number or '0'
        if (file_stream.good())
        {
            break;
        }

        account_num.clear();
        num_length = 1;
    }

    return sfile_path; // the function will return the path of the acoount file
}

// function that checks wether the file exists or not when the user is signing up
string Account::accountExistenceCheckSignUp(string& account_num)
{
    string sfile_path = Paths::USER_INFO + account_num;

    char ch_num;
    int num_length = 1;

    ifstream file_stream(sfile_path);

    // a condition that checks wether the file exists or not, if it doesnt exit it breaks out the loop meaning that the account
    // doesnt exist so it is safe to create an account with that number
    if ( ! file_stream.good())
    {
        return sfile_path;;
    }

    account_num.clear();

    while (true)
    {
        cout << endl << endl << "Account Already Exists!!" << endl;
        cout << "Account Number: ";
        ch_num = getch();

        // if the user wishes to go back to the menu he/she presses esc
        if (ch_num == 27)
        {
            mainMenu();
        }

        while (ch_num == 13 && num_length <= 16)
        {
            if (account_num.empty())
                cout << "\b  \b";

            ch_num = getch();

            // if the user wishes to go back to the menu he/she presses esc
            if (ch_num == 27)
            {
                mainMenu();
            }
        }

        // while the user doesnt hit enter, 13 is the ASCII code for enter
        while (ch_num != 13)
        {
            if (account_num.empty())
            {
                cout << "  \b";
            }

            // when the user hits backspace, 8 s the ASCII code for backspace
            if (ch_num == 8)
            {
                if ( ! account_num.empty())
                {
                    -- num_length;
                    account_num.pop_back();
                }

                cout << "\b \b";
            }
            // if the pin lenght is equal or below 4 asterisk will be displayed to mask the pin and account_num will be take the character input by the user
            // only numbers are accepted
            else if (num_length <= 16 && isdigit(ch_num))
            {
                if (account_num.empty())
                    cout << "\b \b";

                account_num.push_back(ch_num);

                cout << ch_num;

                ++num_length;
            }
            else if ( account_num.empty()) // when the user clear the input this condition won't allow the cursor to improper forward move
            {
                cout << "\b \b";
            }

            ch_num = _getch();

            // if the user wishes to go back to the menu he/she presses esc
            if (ch_num == 27)
            {
                mainMenu();
            }

            while (ch_num == 13 && num_length <= 16)
            {
                if (account_num.empty())
                {
                    cout << " \b";

                }

                ch_num = _getch();

                // if the user wishes to go back to the menu he/she presses esc
                if (ch_num == 27)
                {
                    mainMenu();
                }
            }
        }

        // when the user learns that the account doesnt exist he or she has the optionion of entering '0' and go back to the main menu
        if (account_num[0] == '0' && account_num.size() == 1)
        {
            mainMenu();
        }

        sfile_path = Paths::USER_INFO + account_num;

        ifstream file_stream (sfile_path);

        // the user will keep being noticed that the account doesnt exist until he or she either inputs a valid account number or '0'
        if ( ! file_stream.good())
        {
            break;
        }

        account_num.clear();
        num_length = 1;
    }

    cout << endl;

    return sfile_path;
}


// a function that checks for the existence of a file that is aimed to be transfered an amount
string Account::accountExistenceCheckTransfer(string& transfer_account_number)
{
    string sfile_path = Paths::USER_INFO + transfer_account_number;

    char ch_num;
    int num_length = 1;

    ifstream file_stream(sfile_path);

    if (file_stream.good())
    {
        return sfile_path;
    }

    while (true)
    {
        transfer_account_number.clear();
        num_length = 1;

        cout << endl << endl << "Account Doesn't Exist!!" << endl;
        cout << setw(10) << "To Account Number: ";
        ch_num = _getch();

        if (ch_num == 27)
        {
            ifstream file (file_path);
            myAccount(&file);
        }

        while (ch_num == 13 && num_length <= 16)
        {
            if (transfer_account_number.empty())
                cout << "\b  \b";

            ch_num = getch();

            // if the user wishes to go back to the menu he/she presses esc
            if (ch_num == 27)
            {
                ifstream file (file_path);
                myAccount(&file);
            }
        }

        // while the user doesnt hit enter, 13 is the ASCII code for enter
        while (ch_num != 13)
        {
            if (transfer_account_number.empty())
            {
                cout << "  \b";
            }

            // when the user hits backspace, 8 s the ASCII code for backspace
            if (ch_num == 8)
            {
                if ( ! transfer_account_number.empty())
                {
                    -- num_length;
                    transfer_account_number.pop_back();
                }

                cout << "\b \b";
            }
            // if the pin lenght is equal or below 4 asterisk will be displayed to mask the pin and transfer_account_number will be take the character input by the user
            // only numbers are accepted
            else if (num_length <= 16 && isdigit(ch_num))
            {
                if (transfer_account_number.empty())
                    cout << "\b \b";

                transfer_account_number.push_back(ch_num);

                cout << ch_num;

                ++num_length;
            }
            else if ( transfer_account_number.empty()) // when the user clear the input this condition won't allow the cursor to improper forward move
            {
                cout << "\b \b";
            }

            ch_num = _getch();

            // if the user wishes to go back to the menu he/she presses esc
            if (ch_num == 27)
            {
                ifstream file (file_path);
                myAccount(&file);
            }

            while (ch_num == 13 && num_length <= 16)
            {
                if (transfer_account_number.empty())
                {
                    cout << " \b";

                }

                ch_num = _getch();

                // if the user wishes to go back to the menu he/she presses esc
                if (ch_num == 27)
                {
                    ifstream file (file_path);
                    myAccount(&file);
                }
            }
        }

        while (transfer_account_number == account_num)
        {
            transfer_account_number.clear();
            num_length = 1;

            cout << endl << endl << "Can't Transfer Money To Your Own Account!" << endl;
            cout << setw(10) << "To Account Number: ";
            ch_num = _getch();

            if (ch_num == 27)
            {
                ifstream file (file_path);
                myAccount(&file);
            }

            while (ch_num == 13 && num_length <= 16)
            {
                if (transfer_account_number.empty())
                    cout << "\b  \b";

                ch_num = getch();

                // if the user wishes to go back to the menu he/she presses esc
                if (ch_num == 27)
                {
                    ifstream file (file_path);
                    myAccount(&file);
                }
            }

            // while the user doesnt hit enter, 13 is the ASCII code for enter
            while (ch_num != 13)
            {
                if (transfer_account_number.empty())
                {
                    cout << "  \b";
                }

                // when the user hits backspace, 8 s the ASCII code for backspace
                if (ch_num == 8)
                {
                    if ( ! transfer_account_number.empty())
                    {
                        -- num_length;
                        transfer_account_number.pop_back();
                    }

                    cout << "\b \b";
                }
                // if the pin lenght is equal or below 4 asterisk will be displayed to mask the pin and transfer_account_number will be take the character input by the user
                // only numbers are accepted
                else if (num_length <= 16 && isdigit(ch_num))
                {
                    if (transfer_account_number.empty())
                        cout << "\b \b";

                    transfer_account_number.push_back(ch_num);

                    cout << ch_num;

                    ++num_length;
                }
                else if ( transfer_account_number.empty()) // when the user clear the input this condition won't allow the cursor to improper forward move
                {
                    cout << "\b \b";
                }

                ch_num = _getch();

                // if the user wishes to go back to the menu he/she presses esc
                if (ch_num == 27)
                {
                    ifstream file (file_path);
                    myAccount(&file);
                }

                while (ch_num == 13 && num_length <= 16)
                {
                    if (transfer_account_number.empty())
                    {
                        cout << " \b";

                    }

                    ch_num = _getch();

                    // if the user wishes to go back to the menu he/she presses esc
                    if (ch_num == 27)
                    {
                        ifstream file (file_path);
                        myAccount(&file);
                    }
                }
            }
        }

        sfile_path = Paths::USER_INFO + transfer_account_number;

        ifstream file_stream (sfile_path);

        if (file_stream.good())
        {
            break;
        }
    }

    return sfile_path;
}

// create a set  of files that will contain days and amounts of withdrawal and transfer thta helps ensuring the limit for withdrawal and transfer
void Account::createLimitFiles()
{
    // a file path to where the file that will contain the current day will be saved
    string datefile_path = Paths::ADMIN_WITHDRAWAL_DATE + account_num;

    ofstream date_file (datefile_path); // it is to check the current day to ensure the amount of the withdrawal in one day won't be exceeded

    time_t now = time(nullptr);
    tm* day_check = localtime(&now);

    date_file << day_check->tm_mday;

    date_file.close();

    // a file path to where the file that will contain theamount of withdrawal in one day will be saved
    string valuefile_path = Paths::ADMIN_WITHDRAWAL_VAL + account_num;

    ofstream valueFile (valuefile_path);

    valueFile << 0; // the initial amount is 0

    valueFile.close();

    // a file path to where the file that will contain the current day will be saved
    string datefile_pathTransfer = Paths::ADMIN_TRANSFER_DATE + account_num;

    ofstream dateFileTransfer (datefile_pathTransfer); // it is to check the current day to ensure the amount of the withdrawal in one day won't be exceeded

    dateFileTransfer << day_check->tm_mday;

    dateFileTransfer.close();

    // a file path to where the file that will contain theamount of withdrawal in one day will be saved
    string valuefile_pathTransfer = Paths::ADMIN_TRANSFER_VAL + account_num;

    ofstream valueFileTransfer (valuefile_pathTransfer);

    valueFileTransfer << 0; // the initial amount is 0

    valueFileTransfer.close();
}

// function that gets the user through the 'sign up' steps
void Account::signUp()
{
//    cin.get(); // dont know why but it seems like when it enters the functions signIn and signUp it comes with an ENTER  already pressed

    balance = 0; // balance is set to zero because when the user creates a new account his ir her balance will be zero

    string sname;

    // int arrow = 224;
//    char chName;
//   bool check = false;

    system("cls");

    cout << "\t\t\t Sign Up" << endl << endl;

    cout << "Name: ";
    getline(cin, sname);

    if (sname == "0")
    {
        mainMenu();
    }

    while (sname.empty())
    {
        system("cls");

        cout << "\t\t\t Sign Up" << endl << endl;

        cout << "Name: ";
        getline(cin, sname);

        if (sname == "0")
        {
            mainMenu();
        }
    }

    for (unsigned int i = 0; i < sname.size(); ++i)
    {
        if  (( ! isalpha(sname[i]) && ! isspace(sname[i]) ) || isspace(sname[0]))
        {
            cout << endl << "Invalid Name \n Re-enter: ";
            getline(cin, sname);

            if (sname == "0")
            {
                mainMenu();
            }

            while (sname.empty())
            {
                cout << endl << "Can't Be Empty \n Re-enter: ";
                getline(cin, sname);

                if (sname == "0")
                {
                    mainMenu();
                }
            }

            i = -1;
        }
    }

    system("cls");

    char ch_num;
    int    num_length = 1;

    cout << "\t\t\t Sign Up" << endl << endl;

    cout << "Name: " << sname;

    cout << endl << endl << "Account Number: ";
    ch_num = getch();

    if (ch_num == 27)
    {
        mainMenu();
    }

    // in case the user presses enter right away
    while (ch_num == 13 && num_length <= 16)
    {
        if (account_num.empty())
            cout << "\b  \b";

        ch_num = getch();

        // if the user wishes to go back to the menu he/she presses esc
        if (ch_num == 27)
        {
            mainMenu();
        }
    }

    // while the user doesnt hit enter, 13 is the ASCII code for enter
    while (ch_num != 13)
    {
        if (account_num.empty())
        {
            cout << "  \b";
        }

        // when the user hits backspace, 8 s the ASCII code for backspace
        if (ch_num == 8)
        {
            if ( ! account_num.empty())
            {
                -- num_length;
                account_num.pop_back();
            }

            cout << "\b \b";
        }
        // if the pin lenght is equal or below 4 asterisk will be displayed to mask the pin and account_num will be take the character input by the user
        // only numbers are accepted
        else if (num_length <= 16 && isdigit(ch_num))
        {
            if (account_num.empty())
                cout << "\b \b";

            account_num.push_back(ch_num);

            cout << ch_num;

            ++num_length;
        }
        else if ( account_num.empty()) // when the user clear the input this condition won't allow the cursor to improper forward move
        {
            cout << "\b \b";
        }

        ch_num = _getch();

        // if the user wishes to go back to the menu he/she presses esc
        if (ch_num == 27)
        {
            mainMenu();
        }

        while (ch_num == 13 && num_length <= 16)
        {
            if (account_num.empty())
            {
                cout << " \b";

            }

            ch_num = _getch();

            // if the user wishes to go back to the menu he/she presses esc
            if (ch_num == 27)
            {
                mainMenu();
            }
        }
    }

    // after the account number has been input this will call the function that checks wether the ccount number already exists or not
    file_path = accountExistenceCheckSignUp(account_num);

    system("cls");

    cout << "\t\t\t Sign Up" << endl << endl;

    cout << "Name: " << sname << endl << endl;

    cout << "Account Number: " << account_num << endl << endl;

    char ch_pin;
    int pin_length = 1; // it counts how many characters the user has input into the pin, it is initialized to 1 in case the user hits the backspace

    cout << "Pin: ";
    ch_pin = getch();

    while (ch_pin == 13 && pin_length <= 4)
    {
        if (pin.empty())
            cout << "\b  \b";

        ch_pin = getch();
    }

    // while the user doesnt hit enter, 13 is the ASCII code for enter
    while (ch_pin != 13)
    {
        if (pin.empty())
        {
            cout << "  \b";
        }

        // when the user hits backspace, 8 s the ASCII code for backspace
        if (ch_pin == 8)
        {
            if ( ! pin.empty())
            {
                -- pin_length;
                pin.pop_back();
            }

            cout << "\b \b";
        }
        // if the pin lenght is equal or below 4 asterisk will be displayed to mask the pin and pin_check will be take the character input by the user
        // only numbers are accepted
        else if (pin_length <= 4 && isdigit(ch_pin))
        {
            if (pin.empty())
                cout << "\b \b";

            pin.push_back(ch_pin);

            cout << ch_pin;

            ++pin_length;
        }
        else if ( pin.empty()) // when the user clear the input this condition won't allow the cursor to improper forward move
        {
            cout << "\b \b";
        }

        ch_pin = getch();

        while (ch_pin == 13 && pin_length <= 4)
        {
            if (pin.empty())
            {
                cout << " \b";

            }

            ch_pin = getch();
        }
    }

    // it creates a file that will contain all the account information
    ofstream file(file_path);

    int temp_iterator = 0; // a temporary int variable to ensure that after a space is deleted the letter next will be uppercased and not lowercased

    // this for condition ensures  the names will begin with an uppercase letter and there will be no space between
    for (unsigned int i = 0; i < sname.size(); i++)
    {
        if (! isupper(sname[0]))
            sname[0] = toupper(sname[0]);

        if (isspace(sname[i]))
        {
            sname.erase(i, i - (i - 1));

            sname[i] = toupper(sname[i]);

            temp_iterator = i;

            i += 1; // Be FCKNG CAREFUL, I HAD =+ WRITTEN
        }

        if( isupper(sname[i]) && toupper(sname[0]))
        {
            sname[i] = tolower(sname[i]);

            sname[temp_iterator] = toupper(sname[temp_iterator]);
        }
    }

    // it will write the account needed information to the recently created file
    file << sname << " " << account_num << " " << " " << balance << " " << pin;

    file.close();

    backupAccountFile(file_path);

    system("cls");

    cout << "\a \nAccount Successfully Created!!!";

    Sleep(1000);

    system("cls");

    createLimitFiles();

    ifstream file_stream(file_path); // it enters the just created account file so the user can directly sign in after sign up

    myAccount(&file_stream);
}

// function that gets the user through the 'sign in' steps
void Account::signIn()
{
    cout << "\t\t\t Sign In" << endl << endl;

    //  cin.get(); // dont know why but it seems like when it enters the functions signIn and signUp it comes with an ENTER  already pressed

    char ch_num;
    int num_length = 1;

    cout << "Enter Your Bank Account Number: ";
    ch_num = getch();

    // if the user wishes to go back to the menu he/she presses esc
    if (ch_num == 27)
    {
        mainMenu();
    }

    // in case the user user presses enter right away
    while (ch_num == 13 && num_length <= 16)
    {
        if (account_num.empty())
            cout << "\b  \b";

        ch_num = getch();

        // if the user wishes to go back to the menu he/she presses esc
        if (ch_num == 27)
        {
            mainMenu();
        }
    }

    // while the user doesnt hit enter, 13 is the ASCII code for enter
    while (ch_num != 13)
    {
        if (account_num.empty())
        {
            cout << "  \b";
        }

        // when the user hits backspace, 8 s the ASCII code for backspace
        if (ch_num == 8)
        {
            if ( ! account_num.empty())
            {
                -- num_length;
                account_num.pop_back();
            }

            cout << "\b \b";
        }
        // if the pin lenght is equal or below 4 asterisk will be displayed to mask the pin and account_num will be take the character input by the user
        // only numbers are accepted
        else if (num_length <= 16 && isdigit(ch_num))
        {
            if (account_num.empty())
                cout << "\b \b";

            account_num.push_back(ch_num);

            cout << ch_num;

            ++num_length;
        }
        else if ( account_num.empty()) // when the user clear the input this condition won't allow the cursor to improper forward move
        {
            cout << "\b \b";
        }

        ch_num = _getch();

        // if the user wishes to go back to the menu he/she presses esc
        if (ch_num == 27)
        {
            mainMenu();
        }

        while (ch_num == 13 && num_length <= 16)
        {
            if (account_num.empty())
            {
                cout << " \b";

            }

            ch_num = _getch();

            // if the user wishes to go back to the menu he/she presses esc
            if (ch_num == 27)
            {
                mainMenu();
            }
        }
    }

    // it checks for the existence of the account
    file_path = accountExistenceCheckSignIn(account_num);

    // it checks if the account is locked
    lockAccountCheck();

    ifstream file_stream(file_path);

    file_stream.getline(buffer, 256);

    parseAccountInfo(buffer, name, account_num, balance, pin);

    // char* sParseName = new char[100];

    // sParseName = parseName;

    // decohereLetter(sParseName);

    file_stream.close();

    system("cls");

    cout << "\t\t\t My Account" << endl << endl;

    cout << "Welcome " << name << endl << endl;

    char ch_pin;
    int pin_length = 1; // it counts how many characters the user has input into the pin, it is initialized to 1 in case the user hits the backspace
    string pin_check;

    cout << "Pin: ";
    ch_pin = getch();

    while (ch_pin == 13 && pin_length <= 4)
    {
        if (pin_check.empty())
            cout << "\b  \b";

        ch_pin = getch();
    }

    // while the user doesnt hit enter, 13 is the ASCII code for enter
    while (ch_pin != 13)
    {
        if (pin_check.empty())
        {
            cout << "  \b";
        }

        // when the user hits backspace, 8 s the ASCII code for backspace
        if (ch_pin == 8)
        {
            if ( ! pin_check.empty())
            {
                -- pin_length;
                pin_check.pop_back();
            }

            cout << "\b \b";
        }
        // if the pin lenght is equal or below 4 asterisk will be displayed to mask the pin and pin_check will be take the character input by the user
        // only numbers are accepted
        else if (pin_length <= 4 && isdigit(ch_pin))
        {
            if (pin_check.empty())
                cout << "\b \b";

            pin_check.push_back(ch_pin);

            cout << "*";

            ++pin_length;
        }
        else if ( pin_check.empty()) // when the user clear the input this condition won't allow the cursor to improper forward move
        {
            cout << "\b \b";
        }

        ch_pin = _getch();

        while (ch_pin == 13 && pin_length <= 4)
        {
            if (pin_check.empty())
            {
                cout << " \b";

            }

            ch_pin = _getch();
        }
    }

    int pin_checkCount = 1;
    bool note = false;

    // if the pin input is wrong the pin_check will be cleared
    if  (pin_check != pin)
    {
        pin_check.clear();
    }

    // and the pin lenght will be reset to 1
    pin_length = 1;

    while (pin_check != pin)
    {
        // if the pin input is wrong the pin_check will be cleared
        if  (pin_check != pin)
        {
            pin_check.clear();
        }

        // and the pin lenght will be reset to 1
        pin_length = 1;

        system("cls");

        cout << "\t\t\t My Account" << endl << endl;

        cout << "Welcome " << name << endl << endl;

        // it counts how many passwords input attempts the user has tried, when it equal 3 the account is locked for 24 hours
        if (pin_checkCount == 3)
        {
            cout << endl << "3 Attempts Failed!!" << endl;
            cout << "Your Account Has Been Locked For the Next 24 Hours" << endl;

            cout << endl << "Press enter key to continue";

            note = true; //the note is set to true to log the fact the account got locked

            log(note);
            lockAccount();

            char ch = _getch();

            while(ch != 13)
            {
                ch = _getch();
            }

            mainMenu();
        }

        cout << "Wrong Pin!!" << endl;
        cout << "Pin: ";
        ch_pin = _getch();

        while (ch_pin == 13 && pin_length < 5)
        {
            if (pin_check.empty())
            {
                cout << "\b  \b";
            }

            ch_pin = _getch();
        }

        // while the user doesnt hit enter, 13 is the ASCII code for enter
        while (ch_pin != 13)
        {
            if (pin_check.empty())
            {
                cout << "  \b";
            }

            // when the user hits backspace, 8 s the ASCII code for backspace
            if (ch_pin == 8)
            {
                // if the pin_check isn't empty the pin lenght will decreased amd pin characters too
                if ( ! pin_check.empty())
                {
                    -- pin_length;
                    pin_check.pop_back();
                }

                cout << "\b \b";

            }
            // if the pin lenght is equal or below 4 asterisk will be displayed to mask the pin and pin_check will be take the character input by the user
            // only numbers are accepted
            else if (pin_length <= 4 && isdigit(ch_pin))
            {
                if (pin_check.empty())
                    cout << "\b \b";

                pin_check.push_back(ch_pin);

                cout << "*";

                ++pin_length;
            }
            else if ( pin_check.empty())  // when the user clear the input this condition won't allow the cursor to improper forward move
            {
                cout << "\b \b";
            }

            ch_pin = _getch();

            while (ch_pin == 13 && pin_length <= 4)
            {
                if (pin_check.empty())
                    cout << " \b";

                ch_pin = _getch();
            }
        }

        ++pin_checkCount;
    }

    pin_check.clear(); // the pin_check will be cleared in case another log in is made

    log(note); // it will log the time of the 'sign in'

    myAccount(&file_stream); // once the user is signed into his or her account the function 'myAccount' will be called
}

// function that lets the user navigate into his or her account
void Account::myAccount(ifstream* file_stream)
{
    system("cls");

    while (true)
    {
        char option;

        backupAccountFile(account_num);

        file_stream->getline(buffer, 256);
        parseAccountInfo(buffer, name, account_num, balance, pin);

        file_stream->close();

        cout << endl << "Account: " << name << endl << endl;

        cout << setw(10) << "1 -> Balance";
        cout << setw(22) << "2 -> Withdrawal" << endl << endl;
        cout << setw(10) << "3 -> Deposit";
        cout << setw(20) << "4 -> Transfer" << endl << endl;
        cout << setw(10) << "5 -> Transactions";
        cout << setw(16) << "6 -> Settings " << endl << endl;
        cout << setw(10) << "0 -> Sign Out" << endl << endl;
        cout << setw(17) << "-> ";
        option = _getch();

        while (option != '0' && option != '1' && option != '2' && option != '3' && option != '4' && option != '5' && option != '6')
        {
            option = _getch();
        }

        switch(option)
        {
        case '1':
            logInsideAccount("View Balance");
            viewBalance();
            break;

        case '2':
            logInsideAccount("Withdrawal");
            withdrawal();
            break;

        case '3':
            logInsideAccount("Deposit");
            deposit();
            break;

        case '4':
            logInsideAccount("Transfer");
            transfer();
            break;

        case '5':
            // it logs the activity (View Transaction) made in the account
            logInsideAccount("View Transactions");

            transactions(account_num);
            break;

        case '6':
            logInsideAccount("Enter Settings");
            settings();
            break;

        case '0':
            signOut();

        default:
            myAccount(file_stream);
        }
    }
}

void Account::viewBalance() const
{
    system("cls");

    cout << endl << "Account: " << name << endl << endl;

    cout << "Balance: " << balance << endl << endl;

    cout << "******** Click Enter To Return ********";

    char ch;

    ch = _getch();

    while (ch != 13)
    {
        ch = _getch();
    }

    system("cls");
}


void Account::withdrawal()
{
    system("cls");

    string sAmount;

    long double amount;

    cout << "\t\t\t\t Withdrawal" << endl;

    cout << endl << "Account: " << name << endl << endl;

    cout << endl << " Amount Of Withdrawal: ";
    getline(cin, sAmount);

    for (unsigned int i = 0; i < sAmount.size(); ++i)
    {
        if (! isdigit(sAmount[i]))
        {
            cout << endl << "Invalid Amount!! \nAmount: ";
            getline(cin, sAmount);

            i = -1;
        }
    }

    amount = atof(sAmount.c_str()); // it coverts the string to a float number

    while (amount > balance)
    {
        cout << endl << "Insufficient Balance!!";
        cout << endl << "   Amount Of Withdrawal: ";
        getline(cin, sAmount);

        for (unsigned int i = 0; i < sAmount.size(); ++i)
        {
            if (! isdigit(sAmount[i]))
            {
                cout << endl << "Invalid Amount!! \n  Amount Of Withdrawal: ";
                getline(cin, sAmount);

                i = -1;
            }
        }

        /*
        amount = strtod(sAmount.c_str(), nullptr);
        amount = atof(sAmount.c_str());
        */

        amount = stof(sAmount); // converting string to int
    }

    system("cls");

    char option;

    if (amount != 0)
    {
        cout << endl << "Account: " << name <<  endl << endl;

        cout << "Withdrawal Amount: " << amount << endl << endl;

        cout << setw(18) << "Confirm" << endl << endl;

        cout << setw(10) << "1 -> Yes" << setw(20) << "Other -> No" << endl << endl;

        cout << setw(15) << "-> ";
        option = _getch();

        switch (option)
        {
        case '1':
            withdrawalLimit(amount);
            balance -= amount;
            logTransactions(account_num, "Withdrawal", amount);
            break;

        default:
            withdrawal();
            break;
        }

        ofstream drawFile (file_path);

        drawFile << name << " " << account_num << " " << " " << balance << " " << pin; // updates the information in the account file

        drawFile.close();
    }

    system("cls");
}

void Account::withdrawalLimit(int withdrawalAmount)
{
    dayShiftCheck();

    string sfile_path = Paths::ADMIN_WITHDRAWAL_VAL + account_num;

    char buffer [256];

    int amountDayDrawal;

    ifstream file (sfile_path);

    file.getline(buffer, 256);

    istringstream amountFile (buffer);

    amountFile >> amountDayDrawal;

    file.close();

    int amount_limit = amountDayDrawal + withdrawalAmount;

    ofstream day_amount_file (sfile_path);

    if ( amount_limit > 20000)
    {
        system("cls");
        cout << "\a \n\n\n\n\n\n \t" << "Exceeded The Withdrawal Amount Limit For The Day" << endl << "\t" << "Withdrawal Amount Limit -> 20000" << endl;

        Sleep(2000);

        day_amount_file << amountDayDrawal;

        day_amount_file.close();

        ifstream file_stream (file_path);

        myAccount(&file_stream);
    }
    else
    {
        day_amount_file << amount_limit;

        day_amount_file.close();
    }

}

// not complete

main.cpp

#include <iostream>
#include <windows.h>

#include "classAccount.h"

using namespace std;

int main()
{
     cout << "\n\n\n\n\n\n \t\t\t\t\t" << "Helder Batalha" << endl;

     Sleep(500);

     system("cls");

    Account acc;

    acc.mainMenu();

    return 0;
}

The file classAccount.cpp is not complete as it contains about 3000 lines of codes. So here is the file classAccount.cpp.

\$\endgroup\$
  • 1
    \$\begingroup\$ In order to design review your code design, you would need to present us with a code design. This is code. Code which you have admitted is confusing. My review is that you should watch this Kate Gregory talk, at the very least because she's delightful. \$\endgroup\$ – JohnFilleau Mar 28 at 0:03
  • 7
    \$\begingroup\$ Your title should state what the code does, not your concerns about it. See How to Ask. \$\endgroup\$ – 1201ProgramAlarm Mar 28 at 1:48
6
\$\begingroup\$

Even Windows uses '/' as a path separator nowadays.

const string Paths::BASE_PATH = "C:\\ProgramData\\BankAcount";

The last time you needed to use '\' as a path separator was 15 years ago.

const string Paths::BASE_PATH = "C:/ProgramData/BankAcount";

If you really want to us the '\' then use the RAW character strings.

const string Paths::BASE_PATH = R"(C:\ProgramData\BankAcount)";

Member variables should always be private.

class Account
{
protected:
    std::string name;
    std::string new_name;
    long long int balance;
    std::string account_num;
    std::string file_path;

    std::string pin, new_pin;

    char buffer[256];

   // STUFF
};

Do you trust a stranger that derived his class from your class to main the invariants of the class?

Members should be private and then you provide methods to mutate the state in a way that is logically consistent.


OK. Have not read the whole class. But is this a string?

    char buffer[256];

All these methods on a class call Account.

A lot of methods don't seem to have anything to do with an account!

// Not sure what this is to do with an account?
void createFolder() const;
void createLimitFiles();
void deleteFile(std::string file_to_delete);
void settings();
void mainMenu();

The logging functions don't look like they belong in the account. But rather something that the account would use.

void logTransactions(const std::string& log_account_num, const std::string& transaction_type, const int& amount) const;
void log( bool note ) const;
void logInsideAccount(const std::string& in_account) const;

Do you call a method to withdraw information. Then call the logging functions to record that? When you withdraw someting from the account should the account not simply tell the logger about the action so it can be logged.

If that is the case then you need to pass a logging object to the account (probably in the constructor).


Modern Time formatting works with time and stream:

time_t now      = time(nullptr);
string time_now = ctime(&now);
tm* lock_time   = localtime(&now);


lock << lock_time->tm_mday << " " <<  lock_time->tm_hour << " " <<  lock_time->tm_min;

Can be replaced with:

auto now = std::chrono::system_clock::now();
lock << std::format("%e %I %M", now);

This is very verbose:

if ( ! file_in.good() )
{
    return;
}

A stream when used in a boolean context (like an if statement) will automatically convert itself to a bool based on its state (by calling good()).

if (!file_in) {
    return;
}

You are assuming that a line is less than 256 characters long.

char bufferLine[256];
file_in.getline(bufferLine, 256);

It may be true now. But in the future when your class is modified do you trust the next person to read all the code and make sure it conforms to all your current standards? If this is a limit you want the next programmer to inforce you should put it as a constant that is named in the class.

But alternatively I would use a resizable string so it can read any size of line.

std::string  line;
if (std::getline(file_in, line)) {
   // Correctly read a line of text from `file_in`
}

This is broken.

while (file_in.good() && ! file_in.eof())
{
    c = file_in.get();
    existent_content.push_back(c);
}

The situation. You have one character left in the stream and the file is good. The above condition is good and you enter the loop.

You will read the last character. The state of the stream is still good and the EOF will NOT be set. This is because the EOF flag is not set until you read past the end of file. So you enter the loop again. But this time when you try and read a character it fails (and sets the EOF flag). But you still unconditionally add it to existent_content.

You can write it like this but you need to test that the read worked:

while (file_in.good() && ! file_in.eof()) {
    int c;
    if ((c = file_in.get()) != EOF) {
        existent_content.push_back(c);
    }
}

But this is still considered bad practice. You shoudl loop on a read working.

int c;
while ((c = file_in.get()) != EOF) {
    existent_content.push_back(c);
}

======

Logging

I would set up logging so that each action does its own logging. To do this it needs a logging object that knows what to do with the message. If you define your logging object as a class you can define different styles of logging.

class SimpleMessageLogger
{
    public:
        virtual ~SimpleMessageLogger() {}
        virtual void log(std::string const& message) = 0;
};

class SimpleMessageLoggerTOStdErr: public SimpleMessageLogger
{
    public:
        virtual void log(std::string const& message) override
        {
            std::cerr << time(nullptr) << ": " << message << "\n";
        }
}

class SimpleMessageLoggerTOSystem: public SimpleMessageLogger
{
    public:
        virtual void log(std::string const& message) override
        {
            // Call system logger
        }
}

Then your account needs to be set up to use a logger:

class Account
{
    SimpleMessageLogger& logger;
    int                  balance;

    public:
        Account(SimpleMessageLogger& logger)
            : logger(logger)
            , balance(0)
        {}

        void withdraw(int amount) {
            if (balance < amount) {
                logger.log("OverDrawn. We make money by charging fees");
            }
            balance -= amount;
            std::stringstream message;
            message << "Withdrawal: From account 1123 Amount: " << amount << " New Balance: " << balance;
            logger.log(message.str());
        }
}

int main()
{
    std::unique_ptr<SimpleMessageLogger> logger;
    // Decide what type of logger you want.
    logger = new SimpleMessageLoggerTOStdErr;

    Account. myAccount(*logger);

    myAccount.withdraw(1'000'000);
}
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Nitpick: "Member variables should always be private." isn't true in all cases! :P \$\endgroup\$ – L. F. Mar 28 at 4:12
  • 1
    \$\begingroup\$ @L.F. All rules have exceptions. People who are inexperienced follow all the rules. As you gain experience you learn when the rules can be bent and when they can be broken. \$\endgroup\$ – Martin York Mar 28 at 4:15
  • \$\begingroup\$ Fair enough ... I was thinking along the lines of aggregates, so I took it to mean more like "prefer private member variables to protected ones" \$\endgroup\$ – L. F. Mar 28 at 4:23
  • \$\begingroup\$ @MartinYork Thanks, I have taken into account all of your reviews and changed things accordingly except for one. Are you saying that I have to create a class only for logging? If so, could you edit your answer and give me some example with code on how I would do that? \$\endgroup\$ – HBatalha Mar 28 at 9:05
  • \$\begingroup\$ @HBatalha Yes and done. \$\endgroup\$ – Martin York Mar 28 at 20:31
4
\$\begingroup\$

1) The strings (BASE_PATH, USER_PATH, etc) shouldn't be hardcoded. It is fine to have program defaults but they should be configurable via some configuration file and/or commandline.

2) There is a lot of repetition in cade, say, when you ensure that all directories exist. This is because all string are seperate variables. It'd be better if they were organized in a vector/array of strings. And use an enum to indicate which entry signifies what.

3) Avoid using non portable system calls. Use portable thirdparty free-open source libraries instead. For creating directories use std::filesystem (or boost::filesystem if C++17 is out of scope). Furthermore, std::string isn't quite good representation for filesystem path - better use a dedicated class. Also filesystem delimiter "\" doesn't work on all platforms (e.g., linux) use "/" instead.

4) Don't using namespace std; in headers and avoid in general. Want to shorten cout or string - write using std::cout; and using std::string; so you pull only what you need and not the whole namespace which might accidentally break some code.

5) You should have a separate abstract class that manages user input - whatever is the source of said input. getch isn't a good choice. Nowadays frequently one writes core dll on C++ and interface on C# or whatever language is best for the platform. That's being said, given code can be easily fully implemented on the said languages.

Edit: About requested example for (5).

Generally designing API/interface is a complex topic and you should seek some tutorials and guides online. But I can show something simple.

Imagine you have an external class Controller whose purpose is to process users input and issue requests to the Account class or whatever. The Controller could be cmd-based interface much like you have implemented, a good looking webpage that forwards its data to your Account class, or some sort of socket data interpreter that receives request from some server or whatever.

To encompass all of these one should address Controller as an abstract interface class. The only question is what is its interface and how to work with it.

Basic method (easiest to implement considering current code)

enum class ERequest
{
   DO_STUFF_A,
   DO_STUFF_B,
   EXIT
}
class IController
{
   public:
   virtual ~IController() = default;
   virtual ERequest GetRequest() = 0;
   virtual void GetDataForA(...) = 0;
   virtual void SetOutputForA(...) = 0;
   virtual void GetDataForB(...) = 0;
   virtual void SetOutputForB(...) = 0;
}

And in the code you simply write simple function deals with the controller

IController& rController =...;
while(true)
{
   ERequest rq = rController.GetRequest();
   switch(rq)
   {
      case ERequest::DO_STUFF_A:
      rController.GetDataForA(...);
      // process the request
      rController.SetOutputForA(...);
      break;

      case ERequest::DO_STUFF_B:
      rController.GetDataForB(...);
      // process the request
      rController.SetOutputForB(...);
      break;

      case ERequest::EXIT:
      return;
   }
}

It is far from perfect and this type of interface is still very limiting but it already allows one to better organize code and prepare for future development and various possible interfaces.

And there are other design options:

I. Instead of making abstract interface for controller, make an interface for Account class that should be exposed to controller and simply supply it to the controller designer.

This method is good as long as all methods require a small amount of time for processing - and are function like. But it will cause problems if some methods take a while for processing.

Also it is harder to implement as it requires to make the exported methods to be extra safe. And you need to consider various situations like: "what if it methods are called simultaneously? Is my class thread safe?"

II. Make dual interface. Same as (1) but also account class receives a callback class instance from the controller. This way one can utilize parallelism and asynchronous methods. Nobody wants their interface to be laggy and unresponsive. So each call controller schedules a query, while some worker threads process them and send output via the callback class.

Furthermore, one can run some background analysis and send valuable info to the interface without any query requests.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ How would I go about the topic 5 and have a separate abstract class that manages user input. I can't think of a way to do that, I guess I haven't seen something similar, any example? \$\endgroup\$ – HBatalha Mar 28 at 8:56
  • \$\begingroup\$ @HBayalha hmm... perhaps it is better to give this class an external interface to pass to the the API - so API wouldn't be able to call sensitive functions yet be very flexible... But it is also possible with just an abstract API. Dumb example: abstract interface with two function - receiveNextOrder() that returns some enum order and extra data needed to execute the order; and function sendStatus(...) that reports information to the API. Simply make a procedure that waits for the next order and executes it, and reports result - whereas order can be like "getinfo" or "savedata". \$\endgroup\$ – ALX23z Mar 28 at 9:16
  • \$\begingroup\$ Could you edit your answer and give a very basic code of this just so I can an idea? \$\endgroup\$ – HBatalha Mar 28 at 9:29
  • \$\begingroup\$ @HBatalha I wrote a basic example - as well as provided some ideas for other better but more complex design choices. Check it out. \$\endgroup\$ – ALX23z Mar 28 at 23:32
  • \$\begingroup\$ thanks, I will have to google a bit to find more examples of this design though so I can try to implement in my current code, as I never used something similar. \$\endgroup\$ – HBatalha Mar 29 at 10:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.