I'm wondering if this is even possible in C++. Here's what I did in java a while back:

class Suit {
  public static Suit Hearts = new Suit("Hearts");
  public static Suit Clubs = new Suit("Clubs");
  public static Suit Diamonds = new Suit("Diamonds");
  public static Suit Spades = new Suit("Spades");
  private String value;
 
  private Suit(String suit)
  {
    value = suit;
  }
 
  public String getValue()
  {
    return value;
  }
}

After trying many ways to do this in C++, I keep getting back to the error that:
only static const integral data members can be initialized within a class.
I know I could accomplish something similar with an enum Suits {Hearts, Spades, Diamonds, Clubs} , but I really would like to have the string associated with it.

Does anybody have any idea how to do this?

Recommended Answers

All 6 Replies

Of course, all possible in C++ ;)
That's an improvisation:

/// suite.h (class Suite definition)
class Suite
{
public:
    Suite() {}
    Suite(const Suite& suit):value(suit.value) {}
    static const Suite 
        Hearts,
        Clubs,
        Diamonds,
        Spades;
    /// that's all, add some methods...
    const string& str() const { return value; }
    /// add some syntax sugar:
    operator bool() const { return !value.empty(); }
    bool operator ==(const Suite& suit) {
        return value == suit.str();
    }
    bool operator !=(const Suite& suit) {
        return value != suit.str();
    }
protected:  // there are four colours only
    Suite(const char* suit):value(suit) {}
private:
    string value;
};
inline // add stream output support
ostream& operator <<(ostream& os, const Suite& suit) {
    return os << suit.str();
}
/// suite.cpp (class Suite implementation)
const Suite 
    Suite::Hearts("Hearts"),
    Suite::Clubs ("Clubs"),
    Suite::Diamonds("Diamonds"),
    Suite::Spades("Spades")
    ;
/// test.cpp:
/// more syntax sugar for lazies
const Suite& 
    Hearts = Suite::Hearts,
    Clubs  = Suite::Clubs,
    Diamonds = Suite::Diamonds,
    Spades = Suite::Spades
    ;

int main()
{
    Suite suit;
    suit = Spades;
    Suite card(Clubs);
    if (card && suit == Spades)
        cout << suit << '\t' << card << endl;
    return 0;
}
commented: ArkM, always making the threads fun to read :P +9

OK.

Suite(const Suite& suit):value(suit.value) {}

what exactly does that line MEAN? Specifically the ...:value(suit.value){} part...

This part is so called ctor-initializer-list. In C++ it's a preferred way to initialize class members and the only method to pass constructor arguments to base class(es) constructors (the last feature is not used here). Alas, it's impossible to initialize non-integral types static data members in ctor-initializer-list. That's why we do it separately in lines 32-37.

So

Suite(const Suite& suit):value(suit.value) {}

is the class Suite copy constructor: it creates a Suite object from another object of the same type.

The point is that we must define all needed constructors (default constructor - line #5, copy constructor - line #6) if a class has user-defined constructor (see line #23).

Take into account that I don't want to allow users to construct arbitrary Suite objects (like Suite card("Cheat") ). That's why the constructor defined in line #23 is protected. Now we can initialize four static Suite objects (lines 32-37) and construct all others via the copy constructor.

To allow Suite object arrays we need Suite default constructor (a constructor without parameters).

Oh, as usually {} means copy constructor body (a constructor is a member function). It's inline constructor - no need to call any real functions to construct a Suite object with this constructor.

More questions?..
;)

In addition:
Change lines 40-45 in my code above to

/// more syntax sugar for lazies
const Suite 
    &Hearts = Suite::Hearts,
    &Clubs  = Suite::Clubs,
    &Diamonds = Suite::Diamonds,
    &Spades = Suite::Spades
    ;

Probably it's more effective...
I hope you understand that it's an optional code...

No sir, I understand the rest of it :)
But, just to make sure: Those constructors could have been written like this:

Suite(const Suite & suit)
{
  value = suit.value;
}
Suite(const char* suit)
{
  value = suit
}

but it's just more efficient the other way, because it doesn't initialize value first? Would it actually initialize value first anyway, since it isn't an object?

Thanks a million.

Your constructor variant with assignments has the same effect as mine. However as usually assignments in constructors (or even anywhere) are less effective than initialization with ctor-initializers (and are less clear). Yes, formally in your case the compiler initializes an empty string member then creates a temporary string object (for Suite(const char*) constructor) and performs assignment after that. Besides that, ctor-initializer semantically (and pragmatically) directs a (good) compiler to the best optimization of constructor codes.

That's why ctor-initiaslizer-lists are better than assignments. Whenever you have a choice, select ctor-initializer. Don't forget: a constructor is intended for an object initialization.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.