25

I want to declare string constants that will be used across various classes in the project. I am considering two alternatives

Option 1:

#header file 
class constants{
    static const string const1;
};

#cpp file

const string constants::const1="blah";

Option 2:

#header file 
namespace constants{
    static const string const1="blah";
};

Just wondering what would be a better implementation.

Already looked at

Where to store Class Specific named constants in C++

Where to put constant strings in C++: static class members or anonymous namespaces


UPDATE:

Option 3:

Based on the suggestions from "potatoswatter" and "sellibitze" i currently have the following implementation?

#header file
namespace constants{
    extern const string& const1(); //WORKS WITHOUT THE EXTERN  ***WHY***
};

#cpp file
namespace constants{
   const string& const1(){static string* str = new string ("blah"); return *str;}
}

I'm including the header file where i need to use the constants. Are there any major cons of this implementation?

Community
  • 1
  • 1
Asif Mohammed
  • 790
  • 4
  • 11
  • 25
  • 1
    Option 2 doesn't appear to be a solution. The strings will still be defined separately for each source file, despite appearing in the same namespace. – Potatoswatter Sep 08 '10 at 16:51
  • 4
    Option 2 is a solution if Shishya actually gets the syntax for it correct. (No 'static', and the string can only be declared in the header, but will need to be defined in a source file.) – pkh Sep 08 '10 at 16:52
  • @pkh: syntax is a little trickier than that ;v) – Potatoswatter Sep 08 '10 at 16:55
  • Eh, forgot the extern. Not really "tricky," though. – pkh Sep 08 '10 at 17:00
  • 1
    The update looks fine. `extern` is strictly optional in a function declaration, so that's why it works equally either way. You don't need to use `new`, though. `{ static string str( "blah" ); return str; }` is the usual methodology. – Potatoswatter Sep 09 '10 at 22:06

4 Answers4

14

Update 2 years later:

Every global accessible by more than one source file should be wrapped in an inline function so the linker shares the object between the files, and the program initializes it properly.

inline std::string const &const1() {
    static std::string ret = "hello, world!";
    return ret;
}

The inline function is implicitly extern and may be wrapped in a named namespace or a class, if you like. (But don't use a class just to hold static members, as namespaces are better for that. And don't use an anonymous namespace as that would defeat the linker, and each source would see a different std::string object.)

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • It'll be cleaner in the presence of multiple constants to use namespace constants { ... } in the source file. – pkh Sep 08 '10 at 17:01
  • @Potatoswatter: $2.13.4/2- Whether all string literals are distinct (that is, are stored in nonoverlapping objects) is implementationdefined. The effect of attempting to modify a string literal is undefined. So even if they are defined separately, they may not really occupy as much distinct space in memory if that was the concern. – Chubsdad Sep 09 '10 at 02:19
  • @Chubsdad: The only string literals in OP's code are the initializers to `std::string`. I was referring to the `std::string` objects. – Potatoswatter Sep 09 '10 at 02:34
  • @Potatoswatter: When u say use c string literals are you suggesting that i replace the std::string with char*? Also how would i avoid the Static Initialization Order Fiasco? – Asif Mohammed Sep 09 '10 at 15:20
  • 1
    @Shishya: Yes. Of course, it depends whether you really need `std::string` or not. See that page on how to avoid it. You need to create a function to return the global, instead of directly accessing the global. So, if the reason for `std::string` is localization, there could be a function `std::string const& get_localized_string( int string_index )` which simply returns the globals. That solves the initialization order problem. – Potatoswatter Sep 09 '10 at 18:07
  • @Potatoswatter: Localization is not a concern to me for this project. But i'm c++ newbie, so im not quite clear on why switching to char* would solve the SIOF problem. – Asif Mohammed Sep 09 '10 at 20:30
  • @Shishya: `std::string` is an object with a constructor, so it's invalid before the constructor is called. On the other hand, a string literal is just raw data built into the application binary, and it's required to be valid before any C++ code runs at all. Being initialized by the OS, not C++, it's completely outside the SIOF. – Potatoswatter Sep 09 '10 at 20:47
  • @Potataswatter: please comment on the update where i have listed my current impl based on your suggestion. – Asif Mohammed Sep 09 '10 at 21:55
  • Aren't the parenthesis missing? After the name of the function and before the bracket `inline std::string const &const1 () {`. – LuissRicardo Oct 17 '18 at 21:45
12

All answers that resort to std::string run the risk of dynamically allocating memory for a string literal which is going to remain constant throughout the lifetime of the program (and the binary), so they should be avoided.

sellibitze's answer comes close but it has the problem of declaring it once and then defining it elsewhere, which I don't find elegant and is more work. The best way would be

namespace constants {
    const char * const blah = "blah!"
    const char * const yada = "yada yada!"
}

This is solution is discussed further here.

Community
  • 1
  • 1
legends2k
  • 31,634
  • 25
  • 118
  • 222
9

Neither. I'd go with this:

// header file
namespace constants {
extern const char const1[];
}

// cpp file
namespace constants {
extern const char const1[] = "blah";
}

The header file contains a declaration of const1 with incomplete type but convertible to char const* and the cpp-file contains a definition of the character array with external linkage. There is no dynamic initialization like you have with std::string. So, that's a plus, IMHO.

sellibitze
  • 27,611
  • 3
  • 75
  • 95
  • Shouldn't the code in the .cpp be this instead? `const char constants::const1[] = "blah";`, ie: no `extern` and no `namespace` redeclaration. – Remy Lebeau Sep 09 '10 at 21:59
  • @Remy: *shrug*. Do whatever you like. But there's nothing wrong with the code I posted. You can remove `extern` in the cpp file if you include the header which already declares `const1` to have external linkage. But since in C++ constant variables at namespace scope have internal linkage by default you need to have at least one `extern` in there. What I posted also works without including the header in the cpp file, btw. – sellibitze Sep 10 '10 at 06:15
4

Option 1 achieves the same as Option 2, but in a messier way.

If you're going to use a class that just has static members, especially for global access/constants, use a namespace.

PrettyPrincessKitty FS
  • 6,117
  • 5
  • 36
  • 51