Click here to Skip to main content
15,867,453 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I am studying an example in the book "C++ Gem". in header file pub.h it has one line using "extern" keyword. I try to get more insight on this trick from gurus here:

//header file name is pub.h
class Pub {/*....*/};

extern Pub* gpub; // <==what is the benefit to declare this extern variable? 

class Pub_init{
private:
    static int init_count;
    //static data member shared by all Pub_init objects
    //initialized to 0
public:
    Pub_init();
    ~Pub_init();

}

static Pub_init pub_init; //file scope


What I have tried:

I know this declaration means that gpub is defined in other place:
extern Pub* gpub;


what is the benefit to declare this variable in higher position in the header file pub.h?
of course this pub.h file will be included in many files that uses class Pub.
Posted
Updated 25-Jul-21 8:41am

It is important to realize that
C++
extern Pub* gpub;
is not a declaration of a variable, but a declaration of an external object. To create a valid program, you need an actual declaration (i. e. without 'extern') elsewhere.

If your program just consists of:
C++
int a;
int main() {
   a = 1;
   return a;
}
then all is well, because the first line is a (normal) declaration.

If your program consists of
C++
extern int a;
int main() {
   a = 1;
   return a;
}

then the linker will complain that it cannot find any declaration of a, and therefore doesn't know how to specify a memory address that the program main should use when referencing a. (the compiler doesn't know that - it only cares to know that a is an object of type int)

Note that using extern only really makes sense when you create a library that is used by another program. If used in a library, the statement extern int a; will tell the linker to create external linkage to the object a, so that the actual program knows where to find this object.

So, another way to fix the second code example would be to create a library 'mylib.lib' with a header file containing
C++
// header mylib.h
"extern int a;"
and a source file containing
C++
//source file mylib.cpp
#include <mylib.h>
int a = 1;
and then write your main program as
C++
// source file main.cpp
#include <mylib.h> // contains the extern declaration
int main() {
   return a;
}

This setup will tell the compiler the type of a, and the linker will know that the declaration may not be contained in your main.exe. Linking main.exe to mylib.lib then allows the linker to find the external linkage declaration in mylib.lib.
 
Share this answer
 
v3
Comments
Southmountain 25-Jul-21 13:08pm    
thank you very much for this example!
The topic has been covered pretty well previously. Here's how I deal with global variables. Generally speaking, they are not recommended. In my programs I try to avoid them but a few of them usually find their way into the code. In nearly all cases I find they are pointers to shared memory and rarely actual objects. This is mostly because I work with HPC applications that use lots of threads - dozens on a CPU and thousands on a GPU, and global variables are a bad idea unless you have a lock-free design and while I do, I still try to avoid them.

When I use global variables I do two things. 1) place them in a namespace called global and 2) use a header file macro to declare them. Here's what a typical declaration looks like :
C++
namespace global
{
    Global  SharedData *  pSharedData;
} // namespace
I sometimes use another macro to initialize the data also and it looks like this :
C++
namespace global
{
    GlobalVar( SharedData *, pSharedData, nullptr );
}; // namespace
Here's how those macros are defined :
C++
#pragma once
#define GLOBALS_H
// #include "Globals.h"

// the following needs to be defined ONCE to instatiate the globals
// it should be done prior to including this file.
//#define DEFINE_GLOBALS

#ifdef DEFINE_GLOBALS
#define	Global
#else
#define Global	extern
#endif

// these macros facilitate declaring and initializing a global variable

#ifdef DEFINE_GLOBALS
#define GlobalVar(Type,Variable,Value)		Type Variable=Value
#else
#define GlobalVar(Type,Variable,Value)		extern Type Variable
#endif
As the comment states, the DEFINE_GLOBALS macro needs to be defined one time to declare the instances or instantiate the data. This header file makes it very, very easy to use global variables when you need or want to.
 
Share this answer
 
v2
Comments
Southmountain 7-Aug-21 17:02pm    
thank you for sharing your practice!
It means that it's a pointer to a class instance that is not declared in that file - it's a global variable which the linker will find and reference so all modules will refer to the same variable when they use it.
 
Share this answer
 
Comments
Southmountain 24-Jul-21 20:14pm    
thank you OG!
OriginalGriff 25-Jul-21 0:55am    
You're welcome!
Solution 1 describes what's going on. But as to the benefit, I see none. In fact, I would call this code garbage.

Somewhere, a .cpp will create an instance of Pub and publish the pointer to it in gpub.

If gpub is meant to be a singleton, the pointer to it shouldn't be defined where anyone can just overwrite it, and some mechanism should be used to ensure that only the one instance is created.

And if gpub isn't a singleton, then what's so special about this instance of Pub that it needs to be published in this header? This kind of global variable in a header is almost always a red flag.
 
Share this answer
 
v2
Comments
Southmountain 24-Jul-21 20:14pm    
thank you for your insight!
Greg Utas 24-Jul-21 20:49pm    
You're welcome! C++ has many good and bad practices, partly because it evolved from C, and I'm still learning myself. It's interesting to see someone else running the same gauntlet. :)
Southmountain 24-Jul-21 22:18pm    
your reply leads me to another point on this example. that "extern" statement is not important, the key point is the last line. that example wants to show a technique on "allocation hook" the author claimed...
Greg Utas 25-Jul-21 9:08am    
If the last line that you're referring to is

static Pub_init pub_init; //file scope

then this simply creates an instance of Pub_init when the program starts up.

Pub_init has a global, init_count. I'm guessing that Pub's constructor invokes Pub_init to track of the number of Pub instances. If so, that's not how I'd do it. I'd just declare init_count as static at file scope in the .cpp and have Pub's constructor and destructor increment and decrement it. Having a separate Pub_init class adds no value unless it's doing a fair bit more.
Stefan_Lang 25-Jul-21 4:35am    
It's actually impossible to judge whether the use of extern makes sense in the given example without knowing how and where that header is used. If this is the header of a library, it makes perfect sense. See my solution.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900