views:

312

answers:

5

Let's say I have this program:

class Foo {
 public:
    unsigned int bar () {
        static unsigned int counter = 0;
        return counter++;
    }
};

int main ()
{
    Foo a;
    Foo b;
}

(Of course this example makes no sense since I'd obviously declare "counter" as a private attribute, but it's just to illustrate the problem).

I'd like to know how C++ behaves in this kind of situation: will the variable "counter" in the bar() method be the same for every instance?

+2  A: 

By "be the same for every instance" you mean there will be one instance of this variable shared across each class instance, then yes, that's correct. All instances of the class will use that same variable instance.

But keep in mind that with class variables you have to take things like multi-threading into account in many cases, which is a whole different topic.

dcp
+4  A: 

Yes, counter will be shared across all instances of objects of type Foo in your executable. As long as you're in a singlethreaded environment, it'll work as expected as a shared counter.

In a multithreaded environment, you'll have interesting race conditions to debug :).

Timo Geusch
+1 for mentioning dangers in a multithreaded environment.
Omnifarious
Assuming the compiler does not already handle that for you. The language definition is that the variable is consistant across all calls to the method. Thus it is the compiler job to enforce this, so in multi-threaded languages (next version of C++) it is the compilers job. In this version it depends on the integration of the compiler with the threading library. gcc already has this covered and gurantees that access to the static variable is safe across multiple threads.
Martin York
@Martin York: i.e. static variables in C++0x are guaranteed to be thread local? How interesting, and it might possibly greatly surprise some people. Like a nice static class instance counter. Suddenly you'd be counting instances per thread instead. I can't believe they'd make a change like that. Are you sure?!
Omnifarious
REALLY? That would probably break a huge amount of code. I can't imagine the committee breaking even .001% of existing code.
jmucchiello
@Martin York @jmucchiello: No, they added a new thread_local storage class keyword instead. I figured they were sane: http://en.wikipedia.org/wiki/C%2B%2B0x#Thread-local_storage
Omnifarious
Except even then you still have to be careful with multithreading, as the mechanism that sets the initial value of the local static variable may not be thread safe. (Raymond Chen has an excellent blog on this subject here: http://blogs.msdn.com/oldnewthing/archive/2004/03/08/85901.aspx)
Matt Jordan
Going multithreading is how I actually found out that it is so :)
UncleBens
@Omnifarious: __How__ on earth did you get that from my statement!! That would be a complete change in usage symantics. I am sure that you must be completely off your rocker to come up with that interpretation. No the compiler (at the language level) must gurantee the symantics of the variable. If the language is multi-threaded then it must gurantee that access to the varibale is thread safe (ie atomic. In increment of an int in one thread must be immediately available in another). Currently this is already guranteed by gcc.
Martin York
NOTE: This does not mean that access to methods needs to be guranteed to be thread safe. But it does imply that state can not be cached on a per-processor basis. Note 2: This gurantee is only available if the language has the concept of threads built in (C++ does not, but C++0x will).
Martin York
@Martin York: In other words, memory barrier operations are mandated around every access to a static? OK, that changes no semantics, but might make things slower. I think at least one other person had the same interpretation I did. I can see how it could be interpreted your way too. Perhaps your wording could've been clearer.
Omnifarious
@Omnifarious: No they did not. Only you cam up with that interpretation. Subsequent posts were based on your interpretation. Lary: Fart. Curly: Fire!. Moe: Fire? Where?. Curly: He said fire. Lary: Who said fire. Moe: You said Fire. Lary: Who Me. Curly Yes You.
Martin York
@Martin York, given the context of 'interesting race conditions' and 'solving that problem for you' I don't think my interpretation is in the least unreasonable. Simply putting memory barrier operations around a static does not actually solve most of the interesting race conditions, it just makes them slightly less hair-raising to debug.
Omnifarious
A: 

From The C++ Programming Language (2nd edition), page 200, by Bjarne Stroustrup:

Don't use static except inside [plain] functions (§7.1.2) and classes (§10.2.4).

Greg Bacon
Rules are fantastic. __BUT__ only if used in the correct context. A nieve user may take the quote to hart. If you are goign to quote somthing like that you __must__ include the full context.
Martin York
A: 

You just need to grasp two things:

  1. Static variables are stored in static area of the executing program (which is same as that of global variable).
  2. Scope is limited by the general rules of parentheses.Additionally static variables have internal linkage.
Neeraj
Lifetime is not the life of the program. It is from first use (which may be never) until destruction (which is the inverse order of creation of static variables). Alos note initialization is good as it is part of the function not the class.
Martin York
@niel.. my bad!! i didnt saw that
Neeraj
The last sentence is about namespace scope static variables. Local static variables (like those in the question) have no linkage. (There is no way to refer to local variables from different scopes. How would you refer to a variable defined in main? `main()::v` doesn't work, for instance).
Johannes Schaub - litb
+1  A: 

Your example was a couple lines away from being something you could compile and test:

#include <iostream>
using namespace std;
class Foo {
 public:
    unsigned int bar () {
        static unsigned int counter = 0;
        return counter++;
    }
};

int main ()
{
    Foo a;
    Foo b;

    for (int i=0; i < 10; i++)
      cout<<i<<". "<<a.bar()<<" / "<<b.bar()<<endl;
}

The output looks like this:

0. 1 / 0
1. 3 / 2
2. 5 / 4
3. 7 / 6
4. 9 / 8
5. 11 / 10
6. 13 / 12
7. 15 / 14
8. 17 / 16
9. 19 / 18

So yes, the counter is shared across all instances.

Winder
What's amusing is that your output is a perfect example of how sequence points and execution order work and can result in counterintuitive results.
Omnifarious