Five Alternatives for C++ Class Constants

by Cliff Green, Copyright © 1995, 1998, 1999

This page last
updated on:

May 16, 2001

Contents and Related Links

CodeWrangler Pages

Overview

This article discusses five alternatives for declaring and defining C++ constants that remain locally scoped to a class.

My first experience with declaring and defining class constants used the Gnu C++ compiler, which used to have a convenient but non-standard mechanism:

class Xyz {
public:
  const int SOME_NUM = 1000;
  // ...
};
After unsuccessfully trying to port this to the HP Softbench C++ compiler, I realized it was non-standard and took a couple of days to investigate portable alternatives. Here are my results, written for your enlightenment and enjoyment. Each has it's own use (need) and each has limitations. They are ranked by my estimate of the frequency of typical use.

Note: Since I first wrote this article, the ISO / ANSI C++ standard has defined another method of declaring and defining class constants, which is discussed in the second alternative. The previous example would be written as:

class Xyz {
public:
  static const int SOME_NUM = 1000;
  // ...
};

// definition:
const int Xyz::SOME_NUM;
Also, with the addition of namespaces and the std::vector class to standard C++, many of the needs for the design approaches discussed in this article are no longer present.

Alternative 1

Need:

An integral type of constant literal which can be used both within the class header, inside the class member functions, outside of the class (as an independent constant that is associated with the class), and which takes up no space inside objects of the particular class.

Answer:

Untyped enums - this is the traditional way to specify integral constants.

Example:

class Xyz {
public:
  enum { BIG_LIMIT = 100000, SMALLER_LIMIT=5000 };
private:
  int mXarray[BIG_LIMIT];
  float mYarray[SMALLER_LIMIT];
};

if (something > Xyz::BIG_LIMIT)
// ...
Limitations:

Character strings, floating point, and non-integral constant types cannot be defined inside an enum. Also, the specific type relating to size is not stored with the constant - i.e. unsigned short, unsigned char, long, etc. This should not normally cause problems since the compiler can usually figure out the size and usage by the context. WARNING: Truncations can occur if assigning a large constant to a smaller sized variable:

char x = Xyz::BIG_LIMIT;
This will result in truncation, and the compiler may or may not warn of this depending on the warning level.

Alternative 2

Need:

A typed constant which has class scope (must use ClassName:: as a prefix), can be used as a constant both inside and outside of the class (and outside of the class .C / .cpp file), and takes no object storage space.

Answer:

Declare the constant as static const, then define it in the accompanying .C / .cpp file.

Example 1:

(xyz.h):

class Xyz {
public:
  static const int MAGIC_NUM = 42;
private:
  char mArray[MAGIC_NUM];
  // ...
};
(xyz.cpp):
const int Xyz::MAGIC_NUM;
// ...
(anyfile.cpp):
if (something != Xyz::MAGIC_NUM)
// ...
Example 2:

(xyz.h):

class Xyz {
public:
  static const double MAGIC_NUM;
private:
  // ...
};
(xyz.cpp):
const double Xyz::MAGIC_NUM = 42.0;
// ...
(anyfile.cpp):
if (something != Xyz::MAGIC_NUM)
// ...
Limitations:

In example 1, only integral types (int, long, char, short) can use the static intialization in the declaration. In example 2, the only limitation is that the constant cannot be used within the class declaration as a literal kind of constant. For example:

class Xyz {
public:
  static const double MAX_RANGE;
private:
  int mArray[MAX_RANGE];  // compile error!
}

Alternative 3

Need:

A typed constant of any type which is needed by multiple classes, or within one source (.C / .cpp) file.

Answer:

Use a namespace for the constant. Constants needed for a class or a set of classes can be placed in the same namespace as where the classes are declared, but not declared as a static class member. A constant needed only for a source (.C / .cpp) implementation file but for many functions can be placed in an unnamed namespace (this replaces a traditional need for static constants in both C and C++). This method takes no extra space within an object (obviously, since it is not associated with a particular class or object).

Example:

(xyz.h):

namespace XyzNamespace {

static const double MAX_X = 5000.0;

class Xyz {
  // ...
};

}

(xyz.cpp):
#include "xyz.h"

namespace {
  const int MAX_SIZE = 10;
}

Xyz::someFunction (int x) {
  if (x > MAX_SIZE) // ...
}

Xyz::someOtherFunction (int x) {
  if (x > XyzNamespace::MAX_X) // ...
}
Limitations:

Only when a constant literal is needed for an array declaration within a class.


Alternative 4

Need:

A typed constant visible everywhere. If there's no chance of name conflicts, it can be declared as a global object.

Answer:

Define a global constant somewhere appropriate.

Limitations:

Not recommended - it can cause name conflicts in future source code maintenance as well as duplicate variable definitions (if accidentally defined more than once, such as placing it in a header file included in more than one place). There are many alternatives to global constants and variables, such as placing the constant in a namespace, or using a design technique such as Singleton.


Alternative 5

Need:

A constant member data object contained within an object.

Answer:

A const member data object, initialized through the initialization list of the constructor.

Example:

class Xyz {
public:
  Xyz ();
  const Abc mAbc;  // typically in the private section
};
.C / .cpp file:
Xyz::Xyz () : mAbc (/*ctor arguments for Abc*/) { /* ctor body */ }
Limitations:

The const member data object takes up space for each object. The constant data can only be accessed through an object, rather than as static class member data.


This page constructed by Cliff Green, Copyright © 2001.