Class Inheritance and private containers

Hello,

I'm working on an exercise to create a basic Blackjack card game and have a question about class inheritance and private containers.

If both a parent class and a subclass possess a private container (ie, vector), is there a way to ensure that a class function will use to it's own container?

More Details:
The exercise states to create a Hand class, as well as a Deck class that derives from Hand. It recommends using a vector to hold cards, so I created a private vector in both classes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Hand {
public:
    Hand();
    bool Add(Card* card);
    ...
private:
    std::vector<Card*> hand_cards;
    ...
}

class Deck : public Hand
{
public:
    Deck();
    void Populate();
    ...
private:
    std::vector<Card*> deck_cards;
    ...
}


The issue came up when I tried to add a PrintHand() function to the Hand class.

1
2
3
4
5
6
7
 void Hand::PrintCards() const
{
    std::string full_hand;
    for (auto i : hand_cards)
        full_hand += i->Name() + ' ';
    return full_hand;
}


Calls to test_hand.PrintCards() work fine. But calls to my_deck.PrintCards() result in an empty string. I think it's because Deck inherits PrintCards() but does not redirect it it's own deck_cards vector.

A bad solution?

I came up with a workaround by overriding PrintCards() in the Deck class. However, this duplicates the code by having essentially the same function in both Hand and Deck. The only difference between the functions is the name of the vector in question. I feel like this is a poor solution design-wise, but I'm not sure how to solve it in a more intelligent way.

Any assistance is appreciated. Thanks!
David G.
> create a Hand class, as well as a Deck class that derives from Hand
I don't like that design.
Both have a collection of cards, but think on the operations that you may do on each. For example, you may shuffle the deck and draw a card from it, and you may compute the value of a hand (depending on game rules) and perhaps make sets.

> so I created a private vector in both classes.
wrong, now a Deck has two vectors.

> I think it's because Deck inherits PrintCards() but does not redirect it it's
> own deck_cards vector.
variable names are meaningless to the compiler.
and it does use its own vector, because again, Deck has two vectors.
Thanks for the response.

now a Deck has two vectors.

Yar, I figured this was the problem. The Deck object has it's own vector of cards, as well as a "hidden" vector created when Deck invokes Hand constructor. I imagine this is why a call to my_deck.PrintCards() compiles and executes, but doesn't show the desired results.

> create a Hand class, as well as a Deck class that derives from Hand
I don't like that design.


I don't have a grasp on smart class design so I'm following the hierarchy outlined in the exercise itself. My hope is that even if it's imperfect, it'll serve as a launching point to learn what goes in to good and bad designs. It's already been helpful, even when struggling with roadblocks. Exercise PDF if interested: http://www.montefiore.ulg.ac.be/~hoyoux/info0004/info0004-project1-2010.pdf

Given the constraints, what might be reasonable ways for classes that derive from Hand to contain a vector of cards?

My thoughts are:
1) Exercise proposes creating a Deck, Player and House class that derives from Hand.
2) Each Deck, Player and House should have it's own vector of cards since they logically contain cards.
3) Some functions are common to each subclass. Example: Add(Card* card).
Deck will use Add() when populating the playing cards. Player and House will use Add() when cards are dealt out and hit for an additional card.

I'm lost how to implement a function like Add() or PrintCards() in the base class, while avoiding the situation where each derived class has multiple containers. Is the solution to write an Add() function for each derived class? Wouldn't that lead to code duplication?

Thanks again. If anybody has recommendations or links to learn class design then I'd be happy to explore them. I have some (weak) understanding of the C++ mechanics, but not the "smart" ways to design them.
Last edited on
Maybe I can boil down the question a bit.

If I create a private vector in the base class, every derived class will also contain a vector. Can each derived class then make use of that vector as it's own storage instead of creating a duplicate container?

Thinking aloud:
1) Hand can have a private vector.
2) Create member functions in Hand that use the vector: Add() to add cards, PrintCards() to display those cards on screen, etc.
3) Derived classes that need their own storage will "access" their vector via scoped Hand functions: Hand::Add(), Hand::PrintCards()

So a function like Deck::Populate() could be implemented as like:
1
2
3
for (auto s : all_suits)
    for (auto r : all_ranks)
        Hand::Add(new Card{ r, s });


Gonna play with this a bit. Here's hoping!
Last edited on
Use value semantics; hold a vector of cards by value in the base class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class hand
{
    public:
         bool add( card c ) ;
         bool remove( card c ) ;
         std::size_t count() const { return cards.size() ; }
         bool contains( card c ) const ;
         void print_cards() const ;
         // etc.

    private: std::vector<card> cards ; // vector of cards (not pointers to cards)
};

bool hand::add( card c )
{
    if( contains(c) ) return false ;

    cards.push_back(c) ;
    return true ;
}

class deck : public hand
{
    void populate() ; // call add( card( suit, rank ) ) for each card
    // ...
};
> Derived classes that need their own storage will "access" their vector via
> scoped Hand functions: Hand::Add(), Hand::PrintCards()
that's the idea. Let `Hand' manage its internals, you simply use its interface.
however, there is no need to scope anything, the derived classes have inherit those methods. But if you feel it's more clear that way, do it.


> Use value semantics; hold a vector of cards by value
I suppose that the use of pointers come from
See Card objects as real-life objects. You don't copy a card when you deal it from the deck to a hand; you move it.

so none of this pointers mean ownership (no on the players, no on the deck).
still, no need for dynamic allocation, just changing `Populate()' meaning.

> In the Blackjack game program, de?ne copy constructors and overloaded
> assignment operator member functions to the Hand class and all of its
> subclasses. (Why?)
¿what does it mean to copy a `Hand'?

> if( contains(c) ) return false ;
don't go making wild assumptions.
Thank you kindly for the replies.

With your help, I was able to rework classes so the vector is stored in Hand and accessed by subclasses via interface functions. This required adding extra of functions to Hand, but I see now that it's important to plan out class interfaces to accommodate inheritance.

A related question has come up. Decks need to be shuffled, but player hands do not. So I added a Shuffle() function to the Deck class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Deck{
public:
    ...
    void Shuffle();
private:
    std::random_device rd;        // lifted from web, ignoring details for now
    std::mt19937 rng{ rd() };    
}

void Deck::Shuffle()
{
    if (Size() > 0)
        std::shuffle(BottomIndex(), TopIndex(), rng);  // bottom = begin(), top = end()
    else
        std::cerr << "Debug: Cannot shuffle empty deck\n";
}


To use std::shuffle(), I need to pass along the begin and end points of the vector. I solved this by adding a public accessor function in Hand that returns a vector<Card>::iterator.

1
2
3
4
5
6
7
8
class Hand {
public:
    ...
    std::vector<Card>::iterator TopIndex() { return hand_cards.end(); }
    std::vector<Card>::iterator BottomIndex() { return hand_cards.begin(); }
private:
    std::vector<Card> hand_cards;
}

This approach works; I am able to shuffle my deck. But it feels "dirty" because it grants everybody access to start and end of the vector. It also reveals the details about how Hand has been implemented.

Should TopIndex() and BottomIndex() be protected functions instead? Is there a better design that allows Deck to access the start and end of the vector without exposing details?

Obliged!
> The exercise states to create a Hand class, as well as a Deck class that derives from Hand.

This seems to be a violation of the Liskov Substitution Principle.
https://en.wikipedia.org/wiki/Liskov_substitution_principle

If a player says: 'deal me a hand', we can't just hand over an entire deck.


> Should TopIndex() and BottomIndex() be protected functions instead?

With the hierarchy as it is, it may be simpler to make the vector of cards protected.
Topic archived. No new replies allowed.