0

I have two types of a custom Player class, botPlayer and humanPlayer. I create a vector of the base class Player, and when using functions that are defined in the Player class, the code runs fine.

Code is below:

Player.h

class Player
{
    public:
    explicit Player(string name){
        this->name = name;
        this->balance = 100;
    } 

    Player() { };

int getBalance(){
    return this->balance;
} 

Hand getHand(){
    return this->playerHand;
} 

string getName(){
    return this->name;
} 

void fillHand(vector<Card> &deck){

    for (unsigned int i = 0; i < 5; i++){
        Card curr = deck[i];
        playerHand.addCardToHand(curr, i);
        deck.erase(deck.begin() + i);
    } 
  playerHand.convertHandtoInt();
} 

void updateBalance(int n){       
    this->balance += n;
} 

void placeBet(int &ante, int &pool){       
    pool += ante;      
    updateBalance(-ante);
}

    protected:
    int balance;
    Hand playerHand;
    string name;

}; 


class botPlayer : public Player
{ 
    public:
    explicit botPlayer(string name){
        this->name = name;
        this->balance = 100;
    }

    int decide(){        
        return rand() % 2;
    } 

    void tradeCards(vector<Card> &deck){
        int decision = decide();
        if(decision == 1){
            int trades = rand() % 3 + 1;            
            cout << "Decided to trade " << trades << " cards" << endl;

            for (unsigned int i = 0; i < trades; i++){ 
                playerHand.addCardToHand(deck[i], i);
                deck.erase(deck.begin() + i);
            } 

        }else{
            cout << "Decided not to trade cards" << endl;
        } 
    } 
}; 


class humanPlayer : public Player
{
    public:
    explicit humanPlayer(string name){
        this->name = name;
        this->balance = 100;
    } 

    void tradeCards(vector<Card> &deck){
        string input;
        vector<int> trades;
        int curr;

        cout << "Please enter the indices you want to switch cards for. If you don't want a switch, simply type x" << endl;
        cin >> input;

        for(char& c : input){           
            if(c == 'x'){
                return;
            }else{
                curr = (int)c - 48;
                trades.push_back(curr);
            } 
        } 

        for (unsigned int i = 0; i < trades.size(); i++){ 
            playerHand.addCardToHand(deck[i], trades[i]);
            deck.erase(deck.begin() + i);
        } 
    } 

}; 

Main.cpp

/** Include a bunch of stuff **/
using namespace std;

int main(){
    srand(time(NULL));
    int ante = 50;
    int pool = 0;

    Deck myDeck;
    vector<Card> theDeck = myDeck.getDeck();
    myDeck.shuffleDeck(theDeck);

    humanPlayer playerOne("tony");
    botPlayer playerTwo("comp");

    vector<Player> players;
    players.push_back(playerOne);
    players.push_back(playerTwo);


    while(players.size() > 1){
        Hand bestHand = players[0].getHand();
        checkPlayers(players);
        for(unsigned int i = 0; i < players.size(); i++){
            cout << "My name is: " << players[i].getName() << " and my balance is: " << players[i].getBalance() << endl;
            players[i].fillHand(theDeck);
            players[i].placeBet(ante, pool); 

            cout << " My current hand is: " << endl;
            players[i].getHand().printHand();
            cout << "\n" << endl;

            players[i].tradeCards(theDeck);
            //curr.getHand().printHand();
            //cout << "\n" << endl;
        } //end for
    } //end while

    cout << players[0].getName() << " wins! " << endl;
    return 0;

} //end main

However, when using functions defined in the subclasses, I get an error:

Testing.cpp:43:24: error: '__gnu_cxx::__alloc_traits<std::allocator<Player>, Player>::value_type' {aka 'class Player'} has no member named 'tradeCards'
   43 |             players[i].tradeCards(theDeck);

I tried adding in a blank function call in the Player class, like so:

void tradeCards(vector<Card> &deck){ };

The code then compiled, but produced incorrect results (i.e., the cards were not traded).

How can I access the specific inherited class' member functions? Isn't this supposed to be accomplished by polymorphism?

artemis
  • 6,857
  • 11
  • 46
  • 99
  • You've left out the most important part, the base class method declarations, which should declare `tradeCards` as virtual method, but this is mostly a moot point because, no matter what, you cannot use vectors of base objects like that in C++, because of object slicing. If you're coming from Java background, this is how objects work in Java but not in C++, because C++ is not Java. See the linked question for more information. – Sam Varshavchik Dec 04 '19 at 02:02
  • I left them out to try and make this a minimal question, I will edit it. I _did_ see the object slicing question (originally accessed it from https://stackoverflow.com/questions/8777724/store-derived-class-objects-in-base-class-variables). So if I understand you correctly, what I am trying to do is impossible, then? – artemis Dec 04 '19 at 02:04
  • @JerryM. The usual way of doing something like this is to make the base class polymorphic (giving it a `virtual` destructor), making the interface (i.e. `tradeCards`) a `virtual` function of the base class overridden in the derived classes and then storing it as `std::vector>` or something of that sort. – walnut Dec 04 '19 at 02:13
  • Of course it's not impossible. Everything is possible in C++, but you have to use pointers correctly. – Sam Varshavchik Dec 04 '19 at 02:16
  • So adding `~Player(){ };`, adding `virtual void tradeCards(vectorCard> &deck) { };` to the `Player` class, adding `virtual` keyword in the derived classes, and then using `unique_ptr` smart pointer from ``. Ok, I will try this. Thank you both for the tip. Sorry for duplicate question. – artemis Dec 04 '19 at 02:16
  • @JerryM. `virtual` destructor: `virtual ~Player() = default;`. That is extremely important. Otherwise you will have undefined behavior. – walnut Dec 04 '19 at 02:17
  • The `virtual` keyword in derived classes is not necessary, but it's recommended that `override` method qualifier gets specified, which you can do if you're using a modern C++ compiler. Some Googling around will explain why. – Sam Varshavchik Dec 04 '19 at 02:17
  • I appreciate the support, and again, apologize for duplicate and taking up your time. My course did not go over pointers much other than to "always pass by reference if you can". I will give this a go. Thank you. – artemis Dec 04 '19 at 02:18
  • @JerryM. That is good advice and whether you think of the `std::unique_ptr` inside a `std::vector` as smart pointer or "smart reference" doesn't really matter. The point of it is that when the vector removes an element it guarantees that the thing the `std::unique_ptr` references will also be deleted. However the `std::unqiue_ptr` interface mimics that of a raw pointer, so you need to use e.g. `->` instead of `.` to access members, etc. Polymorphism works with references as well, but `std::vector` does not accept references as elements directly. – walnut Dec 04 '19 at 02:21

0 Answers0