5

EDIT: correct function names, and added #pragma once

This is a very strong simplification of my problem, but if I do this:

A.h

#pragma once
static int testNumber = 10;
void changeTestNumber();

A.cpp

#pragma once
#include "A.h"

void changeTestNumber()
{
    testNumber = 15;
} 

B.h

#pragma once
#include "A.h"
// some other stuff

B.cpp

#pragma once
#include "B.h"
// some other stuff

main.cpp

#pragma once
#include "B.h"
#include <iostream>

int main(){

changeTestNumber();
std::cout<<testNumber<<std::endl;

return 0;
}

Why am I not getting testNumber = 15 at the call out? What really happens when I use a function that is included in a header of my included header? If I remove the static in from of int testNumber, I will get some error about my testNumber being initialized twice.

So is my header compiled twice when I do this?

Thanks in advance!

remi
  • 937
  • 3
  • 18
  • 45
  • 1
    one function is called `changeNumber` (your prototype) and the other is called `changeTestNumber` (the definition) – Goodies Mar 22 '16 at 06:28
  • http://stackoverflow.com/questions/1164167/variable-declaration-in-a-header-file – kkhipis Mar 22 '16 at 06:28
  • TL;DR: Yes, there are multiple "testNumber" variables, one for each header include. Normally, devs will put the static in the cpp file & use "#pragma once" to control how many times a header defines it's values. More info on the many uses of "static" can be found elseware on this site: http://stackoverflow.com/questions/15235526/the-static-keyword-and-its-various-uses-in-c – Jason De Arte Mar 22 '16 at 06:31
  • The function name mistake was a typo, will edit. I have #pragma once in my "real" code, should have put that in too. Thank you will check that link! – remi Mar 22 '16 at 06:57

3 Answers3

9

Aside from the obvious incorrect naming (which I assume was simply a matter of hastily creating an analogous example and is not the actual issue in your code), you need to declare the variable as extern in your .h/.hpp file. You cannot have an extern variable that is also static since the (one of the) use(s) of static is to keep the variable contained to within a single .cpp file.

If you change:

static int testNumber = 10;

in your A.h file to:

extern int testNumber;

and then in your A.cpp file do something like:

#include "A.h"
int testNumber = 10;

Now go ahead and run:

int main() {
    //changeNumber();
    std::cout << testNumber << std::endl; // prints 10
    changeTestNumber(); // changes to 15
    std::cout << testNumber << std::endl; // prints 15
    std::cin.ignore();
    return 0;
}

Be sure to fix the function names!

Goodies
  • 4,439
  • 3
  • 31
  • 57
  • Many good answers here, thank you! This worked:) I'm having a little trouble using this for vectors now, but I'll try more when I I have time before I start asking. – remi Mar 23 '16 at 10:21
7

Goodies and others are certainly correct, but let me put one more step ahead:

  1. static makes the definition local to the translation unit. So defining a static global variable in the header will result in as many copies as the translation units it is included. Unless that's not specifically what you want that's not the way

  2. extern tells the compiler that the global variable exist somewhere, but is not defined and must be searched at link phase. To the linker to succeed, you need to define it somewhere (typically a source file where it make more sense to exist)

  3. Omitting both of them will result in a "multiple definition" linker error, where more than one source includes an header.

Now, the 2nd case has two limitation:

  • it forces you to have a compilable source to instantiate a global object even in the case you are providing a template library (or an "header only" library) making delivery more complex as required
  • It exposes to the so called global initialization fiasco: if you initialize a global object with values taken from other global objects defined elsewhere, since C++ doesn't grant about their order of construction (that ultimately belongs to the way the linker works), you may have trouble in proper initialization and destruction of global objects.

To avoid all this, consider that

  • A global defined function, if explicitly declared as inline can be linked more times and
  • Template functions as well as in-class defined member functions are in-lined by default
  • static local object are created only once, the first time they are encountered

You can define global values in headers by making them static local to functions: like in

inline int& global_val() //note the &
{ static int z = 0; return z; }

the only drawback is that you have always to place a () upon every access.

Since the local value is unique and instantiated upon a call, this will ensure that, if there are dependencies between globals (think to int z=0 as int z=something_else()) they will be created in the order they are needed and destroyed in reverse order, even in case of recursion and multiple threads (since c++14)

Considering the evolution of C++ towards generics and functional paradigms, and considering that placing all sources in a single compilation unit is sometime preferable than linking many sources... Have a think about not using global variables, but replacing them with inlined instatiator functions.


Editing about 2 years later:

C++17 have finally introduced the inline directive for also for variable declarations, just as a syntactic shortcut to the function expansion.

So -today- you can simply write

inline const float PI = 3.14159;
inline int goodies = 25;
Emilio Garavaglia
  • 20,229
  • 2
  • 46
  • 63
1

A.h

extern int testNumber;
void changeNumber();

A.cpp

#include "A.h"

int testNumber = 10;

void changeTestNumber()
{
    testNumber = 15;
} 

B.h

#include "A.h"
// some other stuff

B.cpp

#include "B.h"
// some other stuff

main.cpp

#include "B.h"
#include <iostream>

int main(){

    changeTestNumber();
    std::cout<<testNumber<<std::endl;

    return 0;

}

Please try like that.

mustafagonul
  • 1,139
  • 1
  • 15
  • 32
  • Many good answers here, thank you! This worked:) I'm having a little trouble using this for vectors now, but I'll try more when I I have time before I start asking. – remi Mar 23 '16 at 10:21