// An illustrative example of how one might define a playing card in C++ // Intended to illustrate some commments in response to StackOverflow question: // http://stackoverflow.com/questions/28599150/pass-vector-of-card-into-function-to-print-and-use #ifndef __CARD__ #define __CARD__ #include #include > // We always want libraries to go in an appropriate namespace to avoid collisions. namespace playing_cards { class Card { public: // These could also be declared using "const static" instead of "constexpr" for pre-C++11. // Using "constexpr" increases the ways in which these can be used, though, and also avoids // needing to declare these variables in the *.cpp file, which is handy. // // Defining these improves the API by making code referencing these values clearer than if // they simply used the numeric value (especially since Ace is sometimes higher valued than King). constexpr int ACE = 1; constexpr int JACK = 11; constexpr int QUEEN = 12; constexpr int KING = 13; // Defining these as an enum ensures that an invalid value cannot be supplied. Putting these // inside the outer "Card" class ensures that the values are well scoped (alternatively, // one could use an enum class in C++11 outside of Card to keep the values scoped that way). enum Suit { SPADES, CLUBS, HEARTS, DIAMONDS }; // Constructor with the value and suit. A problem with this particular declaration, though, // is that it makes it trivial to construct an invalid instance of this object. That could // be resolved by throwing an exception in the event of invalid values, allowing invalid // states and providing a way to detect them (what we do here), or making this constructor // private and requiring that this object be instantiated through a factory method that can // fail and return null. The factory approach is typically my preference, but this is a very // small object that we expect to be copyable, and so that is a little bit heavy-handed. // Second to that, I generally prefer objects always be valid and throw an exception if given // invalid values; however, invalid values may be handy in the case of a card to indicate // additional, non-standard cards (or perhaps to indicate a Joker?), so in this specific // case, it is reasonable to defer error handling to the deck or application. Card(int value, Suit suit); Card(const Card& o); ~Card(); bool IsValid() const; std::string ToString() const; // Technically not necessary (the builtin implementation works just fine), but can be illustrative. bool operator==(const Card& o) const; bool color() const; int value() const; Suit suit() const; private: int value_; Suit suit_; }; } // namespace playing_cards std::ostream& operator<<(std::ostream& out, const playing_cards::Card& card); #endif