[LC++]enum, #define, const, static const, or member const???

Carlo Wood carlo at alinoe.com
Wed Apr 3 00:59:05 UTC 2002

On Thu, Mar 28, 2002 at 05:56:17PM +1030, Mark Phillips wrote:
> Hi,
> I want to define a certain class type.  Objects of this
> type will have an attribute called "kind", indicating what
> kind of object it is.  There will be a finite collection of
> different kinds of object.
> There are a number of different ways I could implement this
> idea, but I'm not sure which is best.  One way is to just
> use an integer:
> class myClassTy {
> private:
>    int kindVt;
> public:
>    int kind() const {return kindVt;}
>    bool empty() const {return (kindVt==0);}
>    bool cat() const {return (kindVt==1);}
>    bool dog() const {return (kindVt==2);}
>    myClassTy(int kindAg): kindVt(kindAg) {}
> };
> The problem with this is that here you need to remember that 0 means 
> "empty", 1 means "cat" and 2 means "dog" etc.  And if you ever change
> the numbering, it could cause all sorts of trouble.
> You could do
> #define EMPTY 0
> #define CAT 1
> #define DOG 2
> Or alternatively you could do
> enum kindTy {
>    emptyKind, catKind, dogKind
> };
> Or you could do
> int const emptyKind = 0;
> int const catKind = 1;
> int const dogKind = 2;
> Or you could embed these inside the class definition.  But
> would you access them like:
>    if (kind == myClassTy::catKind)
> or would you access them like:
>    if (kind == myClass.catKind)
> also I notice that the "npos" member of the standard string class is
> a "static" const.  Should I be using static???

You can only embed constants inside a class when you also make
them static.

> So as you can see, there are a miriad of different ways I could do this.
> Can anyone give some guidance about which is best?  Or failing this, the
> advantages and disadvantages of the different methods?

1) Just use an integer

Bad idea, only do this the values are restricted to two values
(0 and 1; or -1 and 0), or to an infinite number of values
that can be devided into two or three 'kind' of groups, for
example: < 0, == 0, and > 0.  Where the actual value of the
int also has a meaning.  Not however that in that case you
are still not typing the literal number 2 (3, 4, ... etc).

2) Using macros for a constant

Never do that.

An exception is probably macros in program of this type:
used in conjunction with code like

    // code here
    // code here

The advantage of this is that both pieces of code are being
checked for syntax and existing variables.  Also, when you
make a typo in the macro name then the compiler will complain.

The reason that a macro is used because it looks similar to
what you have to do when without the feature the code inside
the 'if' doesn't even compile:

    // code here
    // code here

Still having the advantange that the compiler will complain
when you make a typo in the macro name.

3) Using enums

This is the ideal way if it is possible, in your case it is.

- The code becomes more readable (use clear names!).
- While debugging (gdb) you will also see the names of
  the enums.
- You can't make typos without the compiler complaining.
- It is easy to extend without getting confused or
  forgetting to update parts of your code.
- You are told when you forget a value in a switch.
- If the kinds have more grouping in common it is not
  really supported to the actual value of the enums
  to compare (ie, < 0, == 0, > 0).  You can assign the
  integer values to the enums but it becomes less
- You can't have a group like mentioned in the previous
  point with an infinite number of members.
- You can't treat the values also as integers (ie, array
  index) because that is very dangerous and you are not
  allowed to do arithmetics in certain cases.

When you run into the disadvantages then you want to use
a integer constant.

4) Using 'int const'

In all cases where you also use the actual integer
value of the constants.

Disadvantage: you don't see the names in gdb and you
are not told if you forget a value in a switch.

Advantage: you don't have the disadvantage of enums
while still having the advantages 'code becomes more readable'
'You can't make typos without the compiler complaining' and
'It is easy to extend without getting confused or forgetting
to update parts of your code.'

A minor disadvantage is that when you need the values
in more than one compilation unit (source files) then
you can't make the values static, poluting global namespace
(but that also holds for the enum).  However, when you
put the constants in a header file then you get linker
collisions so you need to put them as 'static int const'
in header files, causing them to appear in every compilation
unit seperately or you'd use 'extern' declarations in a
header file, but then they aren't really constants anymore
(no optimization).

5) Using 'static int const'

Doesn't polute the global namespace.  You want this when
the values are completely related to that particular
class (as in your case).  The enum definition should also
be put inside the class for the same reason: not to polute
global namespace (I'd even make it private when possible).

Carlo Wood <carlo at alinoe.com>

More information about the tuxCPProgramming mailing list