<div style='font: ; text-align: left; '>For MSVC6, option 1 is threadsafe, whereas option 2 is not. In fact, this particular "feature" had me debugging this for a week. I combed every inch of my code and largely failed to find the cause of the random server crashes.
The problem is this. The std::string class has an internal buffer to store the characters for the string. So we have 1 global constant, with it's corresponding 1 buffer. So what happens when you make an assignment, or pass the constant into a function?
string test = MY_CONST;
If test was a primitive type, you'd expect test to contain a copy of the constant. Even here, std::string overloads the assignment operator, so normally, you'd expect test to contain a copy of the string, internal buffer included. Obviously, if this were so, I would not have brought the issue up. Microsoft's implementation of std::string (as well as other implementations such as gcc) contain an "optimization" for the class where a new buffer is not allocated until neccessary. That is, test will point to the same character buffer as MY_CONST, until you make a modification to test. Only then, will it allocate a new buffer and copy the MY_CONST buffer over. So
string test = MY_CONST; // test and MY_CONST point to the same buffer
test[0] = 'a'; // now test points to a different buffer.
Obviously, the intention here is saving memory space. One more thing to note. If test is assigned MY_CONST and no changes are made to test, then test will never have to allocate a buffer. It will just point to the MY_CONST buffer. So what happens when test exits the current scope? Obviously it can't deallocate the buffer, since that would invalidate MY_CONST. So how does MY_CONST and test keep track of the buffer memory and when to deallocate it? The answer is reference counting. Whenever MY_CONST is assigned to another string variable, MY_CONST's reference counter is incremented. When a variable exits scope or when it allocates its own buffer, the reference counter is decremented. When the reference counter reaches zero, the string object will know to deallocate the memory.
Normally, this is fine and peachy, in single threaded applications. When we talk multithreaded, things get hairy. Suppose multiple threads pass in MY_CONST to some function or assigns it to some variable, at the same time. Well there would need to be a reference counter increment for each one of those assignments. Since we are talking multithreaded programming, that increment operation MUST BE ATOMIC. Microsoft uses a simple ++ref_count or something like that. ++ref_count IS NOT ATOMIC. Therefore you violate thread safety. Therefore I spend a week and a half debugging server code trying to isolate seemingly completely random server crashes.
That is why given the two options, option 1 would be preferable, if there's any conceivable chance the constant will be used in multithreaded environment.
But this is just a microsoft issue you say? Well no. It turns out that the "lazy allocation" optimization is a fairly common implementation of std::string. As I've said, gcc (at least at one time, I don't know about now) has the same problem. But even if they make the reference counting increments and decrements thread safe by using mutexs, the overhead of using the mutexs may incur significant overhead, depending on how often you assign the constant to a variable or pass it into another function in the application. So even in non-MS environments, I would now tend to shy away from option 2.
So the lesson learned today is that "const type MY_CONST = value;" is still preferable to "#define MY_CONST value", but only if type is either 1) a primitive type, or 2) you have control of the "type" class now and for the foreseeable future. If, like in this case, you don't have control of what the type is doing internally, it can be very dangerous to use an object as a constant.
Regards,
Zhuge Liang</div>