How to work with variable in namespace

35,030

Solution 1

First, your misunderstanding has nothing to do with namespaces, it's only about static. For the rest of this answer I'm going to refer to simply testNum because the fact it's in a namespace is irrelevant.

I'm also assuming you have another file, probably called test.cpp, which also includes test.h and defines the setNum function.

When a variable or function at namespace-scope (i.e. not a class member or local to a function) is declared static it means the entity's name is internal to that file. Formally it has "internal linkage", meaning it can't be referred to by name or linked to from other files (it can be indirectly referred to through a pointer or by passing it as an argument to another function.) That means if several files define static int testNum then each file has its own internal variable with that name, distinct from the testNum in every other file (in fact one file could have static int testnum and another could have static double testnum and another static char* testNum, they'd all be distinct and internal to each file.) If you put a definition like that in header then every file that includes the header has its own testNum.

So with static on your variable in a header you have a different variable called testNum in every file that includes test.h. That means if you set testNum in one file and call a function in a different file which uses testNum it refers to a different variable, which just happens to have the same name.

Because of this, declaring non-const static variables in headers is almost always wrong.

Without static you would have a definition of the testNum variable in every file that includes test.h, which is not allowed: every entity must be defined once and once only in your program. The way to solve that is to declare the variable in the header, but not define it, which you do by telling the compiler the variable is extern:

extern int testNum;  // N.B. no "= 1" here

That tells the compiler there is a variable with "external linkage" called testNum, so when code refers to testNum it will always mean the same variable (not some name with internal linakge that is a different entity in every file.) After declaring an extern variable it is your responsibility to ensure there is exactly one definition provided somewhere in the program, so in exactly one file (i.e. not in a header that gets included in multiple files) you define it:

int testNum = 1;

Solution 2

static at namespace scope is a misnomer, and shouldn't be used. It means simply that the entity declared static has internal name binding; in other words, that the same name in other translation units will refer to a different entity, and in the case of variable definitions, that there will be a separate instance of the variable in each translation unit. It has no effect on lifetime. (All variables declared or defined at namespace scope have static lifetime.)

static at namespace scope is also deprecated. Don't use it.

With regards to declaring a variable in a header: prefix it with extern, and not static. If a variable is declared extern, and there is no initialization, the declaration is not a definition. Of course, in this case, you must provide a definition somewhere (in a single source file). Something along the lines of:

extern int testNum = 5;
int testNum = 5;
int testNum;          //  implicitly initialized with 0.

EDIT:

To clarify somewhat: there is some confusion here between lifetime and name binding:

  • an object has a lifetime (auto, static or dynamic—or temporary, or exception), and
  • a name is bound to an entity; if the name is declared to be a variable, the entity is an object.

Do not confuse the keyword static with static lifetime. (Functions can be static, but functions have no defined lifetime in C++; they're just there.)

The rules regarding these are not very orthognal. Basically, with regards to lifetime:

  • all variables declared at namespace scope have static lifetime, always,
  • variables declared at local scope have auto lifetime unless they are declared static, and
  • variables declared at class scope have the lifetime of the class object which contains them, unless they are declared static. regards to lifetime.

Objects with static lifetime come into being sometime before main, and live until after you return from main.

With regards to name binding:

  • variables declared at namespace scope have external name binding, unless they are declared static, in which case they have internal name binding (but this use of static is deprecated), or if they are const, and are not declared extern,
  • variables declared at class scope have external name binding, even if they are declared static, and
  • variables declared at block scope have no binding.

Finally, there is the question of whether a declaration is a definition or not. If it is a definition, memory is allocated and the object is (or may be) initialized. If it is not a definition, it simply tells the compiler that there is a definition somewhere else for the entity (object) declared in the declaration. In general, a variable declaration is a definition unless it is declared extern and does not have an initializer.

Solution 3

You might want to make sure your code actually has problems before you post it asking what's wrong ;)

I copy/pasted and fixed your typos, and manually did the include:

#include <iostream>
using namespace std;

namespace test{
   static int testNum=5;
   void setNum(int value);
}

void test::setNum(int value){
   testNum=value;
}

int main(){
    test::setNum(9);
    cout<<test::testNum;
}

result:

$ ./a.out 
9

What you haven't said is what else is in your program. If you have more than just main.cpp, and include your test.h, then each .cpp file will have its own copy of testNum. If you want them to share then you need all but one to mark it as extern.

Share:
35,030
dsollen
Author by

dsollen

just a computer geek

Updated on August 20, 2020

Comments

  • dsollen
    dsollen over 3 years

    I think I hvae a fundamental misunderstanding of namespace and/or static variable. But I have tried this test code (typed by hand, forgive typos)

    test.h:

    namespace test{
       static int testNum=5;
       void setNum(int value);
    }
    

    main.cpp:

    #include <test.h>
    
    int test::setNum(int value){
       testNum=value;
    }
    
    int main(){
        test::setNum(9);
        cout<<test::testNum;
    }
    

    when I run this I get the value 5, not 9 as I would have expected. It seems almost as if I have two instances of the testNum variable, but that seems to be the exact opposite of what static should be doing. I'm guessing I've made a mistake in assuming that these features were identical to their java equvilants somehow...

    I also get an error stating that testNum is declared multuple times if I remove the static from my declaration of testNum, could someone explain why that is the case as well?

    Thank you

  • dsollen
    dsollen almost 12 years
    Thank you for the solution (I'll go test that in one second) but for my own enlightenment could you better explain what the problem is? You said that all variables in a namespace are static, but that still leaves me to wonder why in this example my code wouldn't work since all I need is a static variable. Are you staying that I ended up declaring two static variables with the same name each place I referenced testnum?
  • dsollen
    dsollen almost 12 years
    Thank you, that explains my problem. As I thought I was making a wrong assumption of how static worked from my java development, though I looked up static first and thought that c++ static ws the same...one of those situations where you see the answer your expecting I suppose. I would really like my real program to have the static value default to something safe no matter what (even if someone goes and changes my main.h) to ensure I don't have odd functionality. It seems as if extern prevents me from having that gaurentee that it starts with a valid default value. any way to ensure that?
  • Jonathan Wakely
    Jonathan Wakely almost 12 years
    static means several things in C++. For class members it's similar to Java, but you don't have a class member in your program, you have non-member globals, which don't exist in Java.
  • Jonathan Wakely
    Jonathan Wakely almost 12 years
    I have no idea what you mean about extern preventing a "default" value, or "defaulting to something safe". What's wrong with the = 1 value I gave it in my answer?
  • dsollen
    dsollen almost 12 years
    Wouldn't I have to have the testNum=1 done in a method somewhere? definately doable, but it feels unsafe, if someone accidentally deletes that one line in a function somewhere because they didn't realize what it did the code does Bad Things. In my real code I'm working with function pointers and want to default to a no-op method if the pointer isn't loaded from an .SO, and I would feel safer knowing that no matter what, even if someone plays with my main function later, that I always point to my no-op function if nothing else is loaded. It's a minor thing admittedly.
  • Jonathan Wakely
    Jonathan Wakely almost 12 years
    No, you define it at namespace-scope. That isn't setting the variable, it's providing the definition of the variable you declared to be extern. The extern declaration is a promise to the compiler that there will be a definition somewhere in the program ... so you have to define it somewhere. However, with any glboal variables you need to be aware of the static initialization order fiasco (in that context "static" refers to the lifetime of global variables, not to entities that use the static qualifier)
  • John
    John about 2 years
    What about the variables declared constextern const int testnum;?
  • Jonathan Wakely
    Jonathan Wakely about 2 years
    @John, what about them? I don't know what you're asking. At namespace scope, const implies static, but the extern overrides that. So declaring it extern as my answer suggests will still do the right thing.