-2

This is homework related, so I must use a vector to store these objects. I have a base class BankAccount with derived CheckingAccount and SavingsAccount

I ask the user which account they want to input data for, i.e. Checking or savings and then ask for balance and interest rate/transaction fee. Then, using a switch statement, the object is initialized using these inputs and added to the vector vector<shared_ptr<BankAccount>>account_list using a smart pointer.

Next, I ask the user to pick an account type to interact with by adding a transaction of member function credit or debit (overridden from base class to account for a transaction fee in checking or interest rate in savings).

My problem lies here: Depending on the route the user took, either account would have been added in the order that the user chose, so I don't know the position of the object in the vector.

Accounts are added as such (in a switch case):

cout << "\nEnter a unique name for this savings account: \t";
cin >> name_input;
cout << "\nEnter your initial balance: \t" << endl;
cin >> balanceInput;
cout << "\nEnter your interest rate: \t" << endl;
cin >> interestRate_input;
account_list.push_back(new SavingsAccount(balanceInput,name_input,interestRate_input));  //create savings account object
cout << "\nAccount created successfully" << endl;

How can I first find, in a vector of two objects, the type CheckingAccount or type SavingsAccount, then knowing the elements position, call the appropriate member function on it?

This is what I have so far, so excuse any messy code where I was experimenting and trying to figure things out. for example:

cout << "\nEnter the amount to debit: \t" << endl;
cin >> amount;

//this might work but I don't know if this is where the relevant account resides
account_list[0]->debit(amount); 

//here I don't know how to use the element once it is found to call a function on it
vector<shared_ptr<BankAccount>>::iterator
itElem = find_if(account_list.begin(), account_list.end(), HasIdentifier(account_choice));
account_list.itElem -> debit(amount);
//the line above this doesn't work

In the base class I created a string accountName so each account had a unique ID.

Here is the HasIdentifier class that was created for the find_if to search by account name.

class HasIdentifier:public unary_function<BankAccount, bool>
{
public:
    HasIdentifier(string id) : m_id(id) { }
    bool operator()(const BankAccount& c)const
    {
        return (c.getName() == m_id);
    }
private:
    string m_id;
};

This is the entire program below. I am still getting these errors:

error: no matching function for call to object of type 'HasIdentifier'
    if (__pred(*__first))
        ^~~~~~
.../WA4_2/main.cpp:230:19: note: in instantiation of function template specialization 'std::__1::find_if<std::__1::__wrap_iter<std::__1::shared_ptr<BankAccount> *>, HasIdentifier>' requested here
                            itElem = std::find_if(account_list.begin(), account_list.end(), HasIdentifier(account_choice));
                                          ^
.../WA4_2/main.cpp:169:8: note: candidate function not viable: no known conversion from 'std::__1::shared_ptr<BankAccount>' to 'const BankAccount' for 1st argument
            bool operator()(const BankAccount& c)const

I'm not really sure what to do here. If HasIdentifier doesn't work, it was meant to bring the identifier of any object in to be searched for in the vector.

#include <iostream>
#include <vector>
#include <algorithm>


// class for bank account
class BankAccount {          //balance as double protected member so child classes can access
protected:
    double balance = 0;
    std::string account_name = "???";
public:
    BankAccount() {};

    ~BankAccount() {};

    BankAccount(double userBalance, std::string name) {
        if (userBalance >= 0) {                         // constructor to receive initial balance and use it to initialize the balance
            balance = userBalance;
        } else {
            balance = 0;                                //verify balance is greater than or equal to 0, else display error and set balance to 0
            std::cout << "\nError, balance set to 0\n";
        }
        account_name=name;
    }

    const std::string &getAccount_name() const {
        return account_name;
    }

    void setAccount_name(const std::string &account_name) {
        BankAccount::account_name = account_name;
    };

    virtual void
    credit(double amount) {                            //   Member function credit should add an amount to the current balance.
        balance += amount;
    }

    virtual void
    debit(double amount) {                             //  Member function debit should withdraw money from the Bank-Account and ensure that the debit amount does not exceed
        if (amount >
            balance) {                               //  the Bank-Account’s balance. If it does, the balance should be left unchanged and the function should print the message
            std::cout << "The balance is less than the debit amount.\n";     //  “The balance is less than the debit amount.”
        } else {
            balance -= amount;
        }
    }

    virtual double getBalance() {                      // Member function getBalance should return the current balance.
        return balance;
    };
    std::string getName()const {
        return this->account_name;
    }

    bool operator ==(const BankAccount& compare) const{
        return this->account_name == compare.account_name;
    }
};


class SavingsAccount : public BankAccount {               // inherit bank account class to create savings account
    double interestRate = 0;

public:                                                   // constructor to get initial balance and interest rate
    SavingsAccount(double userBalance, const std::string &name, double user_rate) : BankAccount(userBalance, name),
                                                                                  interestRate(user_rate) {
        interestRate = user_rate;
    }

    double
    calculateInterest() {               // calculateInterest that returns a double indicating the amount of interest earned by an account
        double earnings = 0;            // this amount is calc by multiplying the interest rate by the bank account balance
        double a = 0;
        a = getBalance();
        earnings = a * interestRate;
        return earnings;
    }
    void credit(double amount) {
        balance += amount +
                 calculateInterest();                            // Member function credit should add an amount to the current balance.
    }
};

class CheckingAccount : public BankAccount {
    double transactionFee;
public:
    CheckingAccount(double userBalance, const std::string &name, double transfee_input) : BankAccount(userBalance, name),
                                                                                     transactionFee(
                                                                                             transfee_input) {
        transactionFee=transfee_input;
    }

    void credit(double amount) {
        balance += amount + transactionFee;    //   Member function credit should add an amount to the current balance.
    }

    void debit(double amount) {                                         //  Member function debit should withdraw money from the Bank-Account and ensure that the debit amount does not exceed
        if (amount >
            getBalance()) {                                    //  the Bank-Account’s balance. If it does, the balance should be left unchanged and the function should print the message
            std::cout << "The balance is less than the debit amount.\n";     //  “The balance is less than the debit amount.”
        } else {
            balance -= amount + transactionFee;
        }
    }
};



class HasIdentifier:public std::unary_function<BankAccount, bool>
{
public:
    HasIdentifier(std::string id) : m_id(id) { }
    bool operator()(const BankAccount& c)const
    {
        return (c.getName() == m_id);
    }
private:
    std::string m_id;
};

int main() {
    double balanceInput{0};                        //variables for getting balance/interest inputs/outputs
    double balanceOutput{0};
    double interestRate_input{0};
    double fee_input{0};
    std::string name_input = "???";

    std::vector<std::shared_ptr<BankAccount>>account_list;        //storage for accounts


        std::cout << "\nWhat type of account would you like to input? "
                  << "\nSavings (1)"
                  << "\nChecking (2)"
                  << "\nEnter your choice:\t"
                  << std::endl;
        int choice{0};
        std::cin >> choice;
        switch(choice) {
            case 1: {                                                      //savings input
                std::cout << "\nEnter a unique name for this savings account: \t";
                std::cin >> name_input;
                std::cout << "\nEnter your initial balance: \t" << std::endl;
                std::cin >> balanceInput;
                std::cout << "\nEnter your interest rate: \t" << std::endl;
                std::cin >> interestRate_input;
                account_list.emplace_back(new SavingsAccount(balanceInput, name_input,
                                                             interestRate_input));  //create savings account object
                std::cout << "\nAccount created successfully" << std::endl;
                break;
            }
            case 2: {
                std::cout << "\nEnter a unique name for this checking account: \t";
                std::cin >> name_input;
                std::cout << "\nEnter your initial balance: \t" << std::endl;             //checking account input
                std::cin >> balanceInput;
                std::cout << "\nEnter the transaction fee: \t" << std::endl;
                std::cin >> fee_input;
                account_list.emplace_back(new CheckingAccount(balanceInput, name_input,fee_input)); //create checking account object
                std::cout << "\nAccount created successfully" << std::endl;
                break;
            }
            default: {
                std::cout << "\nInvalid entry" << std::endl;
                break;
            }
        }


    std::cout << "\nTo enter a transaction for your account,"
              << "\nplease enter the account name: \t";
    std::string account_choice="???";
    std::cin >> account_choice;
    std::vector<std::shared_ptr<BankAccount>>::iterator
            itElem = std::find_if(account_list.begin(), account_list.end(), HasIdentifier(account_choice));

    std::cout << "\nWould you like to process a (d)ebit or (c)redit to the account?" << std::endl;
        int a = 0;
        tolower(a);
        std::cin >> a;
        double amount{0};
            switch (a){
                case 'd': //debit the account
                    std::cout << "\nEnter the amount to debit: \t" << std::endl;
                    std::cin >> amount;
                    (**itElem).debit(amount);
                    break;
                case 'c':  //credit the account
                    std::cout << "\nEnter the amount to credit: \t" << std::endl;
                    std::cin >> amount;
                    (**itElem).credit(amount);
                    break;
            default:
                std::cout << "\nInvalid entry" << std::endl;
            }

    return 0;
}

1 Answers1

0

Iterators themselves act like pointers (i.e.: their interface is designed to resemble a pointer), so if you have an iterator of a vector, and want to call a method on the underlying object, you do so "as-though" the iterator were a pointer to the object in question.

struct my_struct {
    int val;
    double other;
    void func() {}
};

int main() {
    std::vector<my_struct> my_structs = /*...*/;

    std::vector<my_struct>::iterator it = std::find_if(my_structs.begin(), my_structs.end(), [](my_struct const& m) {return m.val == 5;});

    it->func();
}

The problem, in your case, is that you're using std::vector<std::shared_ptr<BankAccount>>. Now, based only on the code provided, it looks like some kind of polymorphism is taking place, so I suppose some use of pointers is absolutely necessary (though you should always prefer unique_ptr over shared_ptr when given a choice), but because the vector itself is saving pointers, you need to multiply-indirectly access the underlying object.

std::vector<std::shared_ptr<BankAccount>>::iterator
itElem = std::find_if(account_list.begin(), account_list.end(), HasIdentifier(account_choice));
(**itElim).debit(amount);
//OR...
itElim->get()->debit(amount);

I have no idea what HasIdentifier is or what it's supposed to do: based on that syntax, I'm very wary that it's another source of potential bugs. But it's possible that that portion of code has been written correctly, and without seeing how it's implemented, I can't analyze it anyways. At any rate, the adjustments I've proposed should fix this error.

EDIT: Alright. HasIdentifier. This is a pretty easy fix: the signature of the functor called by find_if is supposed to take the form bool operator()(T t) const, or bool operator()(T const& t) const or bool operator()(T & t) const or some other flavor of that, where T is the type you've instantiated your std::vector with.

So in your case, your std::vector is of type std::vector<std::shared_ptr<BankAccount>>, which means T is std::shared_ptr<BankAccount>. But in your functor, you've defined the signature as bool operator()(BankAccount const& account) const, and std::shared_ptr<T> will not implicitly convert to T& or T const&.

Just update the functor to use std::shared_ptr<BankAccount> const& instead:

class HasIdentifier //No need to inherit from unary_function; it's been removed from C++17 anyways
{
public:
    HasIdentifier(string id) : 
    m_id(std::move(id)) //You're passing by value anyways, so move-constructing m_id 
    //will improve performance, especially if it's a large string.
    {}

    bool operator()(std::shared_ptr<BankAccount> const& c)const
    {
        return (c->getName() == m_id);
    }
private:
    string m_id;
};

One last thing: Stop using using namespace std.

Xirema
  • 19,889
  • 4
  • 32
  • 68