2017-09-08 4 views
-2

Ceci est lié aux devoirs, donc je dois utiliser un vecteur pour stocker ces objets. J'ai une classe de base BankAccount avec CheckingAccount et SavingsAccountC++ Rechercher un objet dans un vecteur et y appeler une fonction membre

Je demande à l'utilisateur le compte pour lequel il veut entrer des données, c'est-à-dire vérifier ou faire des économies, puis demander le solde et le taux d'intérêt/frais de transaction. Ensuite, en utilisant une instruction switch, l'objet est initialisé en utilisant ces entrées et ajouté au vecteur vector<shared_ptr<BankAccount>>account_list en utilisant un pointeur intelligent. Ensuite, je demande à l'utilisateur de choisir un type de compte avec lequel interagir en ajoutant une transaction de la fonction membre credit ou debit (substituée à la classe de base pour tenir compte des frais de transaction en vérification ou du taux d'intérêt en épargne).

Mon problème est le suivant: Selon l'itinéraire emprunté par l'utilisateur, l'un ou l'autre compte aurait été ajouté dans l'ordre choisi par l'utilisateur, donc je ne connais pas la position de l'objet dans le vecteur.

comptes sont ajoutés en tant que tel (dans un boîtier de commutation):

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; 

Comment puis-je trouver d'abord, dans un vecteur de deux objets, le type CheckingAccount ou tapez SavingsAccount, sachant alors la position des éléments, appel la fonction de membre appropriée sur elle?

C'est ce que j'ai jusqu'ici, donc excusez n'importe quel code désordonné où j'expérimentais et essayais de comprendre les choses. par exemple:

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 

Dans la classe de base I créé un string accountName de sorte que chaque compte avait un identifiant unique.

Voici la classe HasIdentifier qui a été créée pour find_if à rechercher par nom de compte.

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; 
}; 

Voici l'intégralité du programme ci-dessous. Je reçois toujours ces erreurs:

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 

Je ne sais pas quoi faire ici. Si HasIdentifier ne fonctionne pas, il était destiné à amener l'identifiant de tout objet à rechercher dans le vecteur.

#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; 
} 
+0

@ RobertPrévost Pas un doublon de cela. Le problème est dû à la façon dont l'utilisateur utilise (de manière irresponsable) les pointeurs, et non au fonctionnement des itérateurs. – Xirema

+0

Pourriez-vous suggérer un meilleur moyen d'utiliser des pointeurs? –

Répondre

0

itérateurs eux-mêmes agissent comme des pointeurs (ex: leur interface est conçue pour ressembler à un pointeur), donc si vous avez un itérateur d'un vecteur, et que vous voulez appeler une méthode sur l'objet sous-jacent, vous le faites " comme si "l'itérateur était un pointeur vers l'objet en 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(); 
} 

Le problème, dans votre cas, est que vous utilisez std::vector<std::shared_ptr<BankAccount>>. Maintenant, basé uniquement sur le code fourni, il semble qu'un polymorphisme se produise, donc je suppose que l'utilisation de pointeurs est absolument nécessaire (bien que you should always prefer unique_ptr over shared_ptr when given a choice), mais parce que le vecteur lui-même économise des pointeurs, vous devez multiplier-indirectement accéder à l'objet sous-jacent. J'ai

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); 

aucune idée de ce HasIdentifier est ou ce qu'il est censé faire: basé sur cette syntaxe, je suis très prudent que c'est une autre source de bugs potentiels.Mais il est possible que cette partie du code ait été écrite correctement, et sans voir comment elle est implémentée, je ne peux pas l'analyser de toute façon. En tout cas, les ajustements que j'ai proposés devraient corriger cette erreur.

ÉDITION: Correct. HasIdentifier. Ceci est une jolie solution facile: la signature du foncteur appelé par find_if est censé prendre la forme bool operator()(T t) const ou bool operator()(T const& t) const ou bool operator()(T & t) const ou une autre saveur de cela, où T est le type que vous avez instancié votre std::vector avec.

Donc dans votre cas, votre std::vector est de type std::vector<std::shared_ptr<BankAccount>>, ce qui signifie T est std::shared_ptr<BankAccount>. Mais dans votre foncteur, vous avez défini la signature comme bool operator()(BankAccount const& account) const, et std::shared_ptr<T> ne sera pas implicitement converti en T& ou T const&.

actualisateurs foncteur utiliser std::shared_ptr<BankAccount> const& à la place:

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; 
}; 

Une dernière chose: Stop using using namespace std.

+0

en vérifiant 'itElem! = My_structs.end()' et '* itElem' serait bien aussi.A présent, nous pouvons utiliser' auto' au lieu de 'std :: vector > :: iterator'. – Jarod42

+0

Merci pour l'aide jusqu'à présent. Pourquoi recommandez-vous d'arrêter d'utiliser 'namespace std'? –

+0

@MarcReinecker Le lien que j'ai fourni justifie sa propre thèse. – Xirema