Card class for card games I'm attempting to improve my coding style by concentrating on simple and focused OOP client-server interfaces and error handling - atm in the context of designing a Blackjack game.  I'm starting with the Card class that represents an individual card.  I'm looking for any improvements on or flaws with my approach.

#include &lt;sstream&gt;
#include &lt;stdexcept&gt;
#include &lt;string&gt;

class Card
{
public:
    enum Ranks { Ace = 1, Ten = 10, Jack, Queen, King };
    enum Suits { Spades, Hearts, Diamonds, Clubs };

    Card( int rank, int suit ) throw(std::range_error)
    {
            if( rank &lt; 1 || rank &gt; 13 ) {
                    std::stringstream errMsg;
                    errMsg &lt;&lt; "Card(): rank '" &lt;&lt; rank &lt;&lt; "' out of range";
                    throw std::range_error( errMsg.str() );
            } else if( suit &lt; 0 || suit &gt; 3 ) {
                    std::stringstream errMsg;
                    errMsg &lt;&lt; "Card(): suit '" &lt;&lt; suit &lt;&lt; "' out of range";
                    throw std::range_error( errMsg.str() );
            }

            m_rank = rank;
            m_suit = suit;
    }

    int rank() const { return m_rank; }
    int suit() const { return m_suit; }
    std::string str( bool useStrings = false ) const
    {
            std::stringstream ret;
            if( useStrings )
                    ret &lt;&lt; sRanks[m_rank] &lt;&lt; " of " &lt;&lt; sSuits[m_suit];
            else
                    ret &lt;&lt; cRanks[m_rank] &lt;&lt; cSuits[m_suit];
            return ret.str();
    }

private:
    static const char cRanks[14];
    static const char cSuits[4]
    static const std::string sRanks[14];
    static const std::string sSuits[4];

    int m_rank;
    int m_suit;
};

const std::string Card::sSuits[4] = {"Spades", "Hearts", "Diamonds", "Clubs"};
const std::string Card::sRanks[14] = { "\0", "Ace", "Two", "Three", "Four", "Five",
                    "Six", "Seven", "Eight", "Nine", "Ten", "Jack", "Queen", "King" };
const char Card::cSuits[4] = { 's', 'h', 'd', 'c' };
const char Card::cRanks[14] = { '\0', 'A', '2', '3', '4', '5', '6', '7',
                                '8', '9', 'T', 'J', 'Q', 'K' };


The client can create a card either using the Card enum constants or integers, like

Card card(Card::Ace, Card::Diamonds);
Card card2(0, 3); // throws an exception


I'm dubious on how useful the public enum constants are for client code.  Also, I'm very new to exception programming and I'm not sure if this is a good approach or whether the constructor should throw an exception.  Any improvements or suggestions?
If you are going to go to the trouble of creating Rank and Suit enum then use them.

Card( int rank, int suit ) throw(std::range_error)


I would have done

Card(Rank rank,Suit suit )


That way you can't accidentally go: Card(Card::Hearts,2) The compiler type checking will catch this at compiletime. Just use the enums all through your code. C++ is a strongly typed language use the compiler to catch your errors.

The exception specifications have been deprecated. They were mostly useless anyway (if you fail and throw an illegal exception it causes your application to terminate without unwinding the stack). So in general don't use them. For the one case where they are usfull (no throw specifications) they are good but I use them more as documentation to remind me that I should make sure the method does not throw.

But yes the constructor should throw an exception if it gets a value outside the expected range. There is no point in allowing an invalid object to propagate threw your code it will just cause problems with your invariants.

Prefer to use the initializer list rather than initializing variables in the constructor.

Card(Rank rank, Suit suit)
    : m_rank(rank)
    , m_suit(suit)


Again another place that it would have been nice to use the enum to make the code more eadable:

        if( rank &lt; 1 || rank &gt; 13 ) {


Try:

        if( rank &lt; Ace || rank &gt; King ) {


Hate getters/setter they spoil encapsulation. Do you really need to get the values of the card? Any operation that manipulates the card is usually a method on the card or a friendly class.

int rank() const { return m_rank; }
int suit() const { return m_suit; }


Don't try and write C++ like Java. We don;t need a string function. It is much more preferable (and flexable) to write an output (and probably input) stream operator.

std::string str() const
{
        return std::string( Ranks[m_rank] + Suits[m_suit] );
}


I would have done:

friend std::ostream&amp; operator&lt;&lt;(std::ostream&amp; stream, Card const&amp; card)
{
    return stream &lt;&lt; card.Ranks[card.m_rank] &lt;&lt; card.Suits[card.m_suit];
}


Adding Cards to a deck. (Based on comment below)

#include &lt;vector&gt;

class Card
{
    public:
        enum Ranks { Ace = 1, Ten = 10, Jack, Queen, King };
        enum Suits { Spades, Hearts, Diamonds, Clubs };

        Card(Ranks rank, Suits suit){}
};

// Need to add the ++ operators for Ranks/Suits
Card::Ranks&amp; operator++(Card::Ranks&amp; r)
{
    return r = Card::Ranks(static_cast&lt;int&gt;(r)+1);
}
Card::Suits&amp; operator++(Card::Suits&amp; s)
{
    return s = Card::Suits(static_cast&lt;int&gt;(s)+1);
}



int main()
{
    std::vector&lt;Card&gt;   deck;

    // Does not look that verbose to me.
    for(Card::Ranks rank=Card::Ace;rank &lt;= Card::King;++rank)
    {
        for(Card::Suits suit=Card::Spades;suit &lt;= Card::Clubs;++suit)
        {
            deck.push_back(Card(rank, suit));
        }
    }

    // Compared to 
    for(int rank=Card::Ace;rank &lt;= Card::King;++rank)
    {
        for(int suit=Card::Spades;suit &lt;= Card::Clubs;++suit)
        {
            deck.push_back(Card(rank, suit));
        }
    }
}