Andry
Andry

Reputation: 16885

Understanding how to correctly treat c++ class constants

Consider the following:

namespace MyNamespace{
class MyClass {
public:
   // Public area
private:
   // Private area
protected:
   // Protected area
}; /* Class */
} /* Namespace */

And consider that I would like to define a constant which is specific for my class. I usually do the following:

namespace MyNamespace{
// Constants
const int MYINT = 12;
const std::string MYSTR = std::string("Hello");
// Class definition
class MyClass {
public:
   // Public area
private:
   // Private area
protected:
   // Protected area
}; /* Class */
} /* Namespace */

In this way I can get my variable in this way (somewhere in my code):

MyNamespace::MYINT;
MyNamespace::MYSTR;

Is this a good practice?
Considering that constants can be treated in several ways (for example numeric constants are often treated using enum), what is the best approach to define a constant (related to a class, but that can be also useful somewhere else) ?

Upvotes: 24

Views: 52062

Answers (3)

wohlstad
wohlstad

Reputation: 29297

I would like to define a constant which is specific for my class

If the constant is specific to the class, it is best to put it in the class itself rather than in the namespace containing the class.

Since C++17 I think the best way of defining a class constant (using inline variables) is:

class MyClass 
{
public:
    // Constants declarations:
    static inline constexpr int MYINT = 12;
    static inline constexpr std::string MYSTR = "Hello";  // Use `const` instead of `constexpr` for C++17
};

static means it has one copy for the class (i.e. it's not a member per instance).
inline means it does not require a separate definition (with an initialization value).
constexpr means it is a compile time constant.

Note that inline in not mandatory here and was mentioned just for being explicit, because according to the spec:

A function or static data member declared with the constexpr or consteval specifier is implicitly an inline function or variable.

(emphasis is mine)

Also note that std::string has a constexpr constructor only from C++20.
If you are using C++17 you should change it for MYSTR to const instead.

Live demo

Upvotes: 3

Sarfaraz Nawaz
Sarfaraz Nawaz

Reputation: 361762

If you want the constants specific to the class and also want them to be useful somewhere else as you said, possibly outside the class, then define them as static member data in the public section of the class:

//.h file
class MyClass 
{
 public:
   //constants declarations
   static const int MYINT;
   static const std::string MYSTR;
};

//.cpp file
//constants definitions 
const int MyClass::MYINT = 12;
const std::string MyClass::MYSTR = std::string("Hello");

Usage (or access):

std::cout << MyClass::MYINT << std::endl;
std::cout << MyClass::MYSTR << std::endl;

Output:

12
Hello

Online Demo: http://www.ideone.com/2xJsy


You can also use enum if you want to define many integral constants and all of them are somehow related, for example this:

class shirt
{
 public:
   //constants declarations
   enum shirt_size
   {
        small, 
        medium,
        large,
        extra_large
   };
};

But if the integral constants are not related, then it wouldn't make much sense to define them as enum, in my opinion.

Upvotes: 41

Jon
Jon

Reputation: 437794

There is no "best" solution as of course that is a very subjective term.

Considering that you mention the constants being used somewhere else, we can say that they should be declared in either the protected (if they are to be used exclusively by derived classes) or more likely the public section of the class.

Constants that are not of integer type should be defined as static const members (but you will have to be careful of the order of static initialization if there are any other static objects that refer to these constants).

Constants of integer type can either be declared as static const int or as enums, as you already mention. The discriminating factor here is whether two or more constants can be logically grouped together.

For example, this is probably a good idea:

class MyClass {
    public:
        enum {
            Color_Red,
            Color_Green,
            Color_Blue,
        };
};

While this is not:

class MyClass {
    public:
        enum {
            Color_Red,
            Vehicle_Car,
        };
};

Upvotes: 5

Related Questions