Using the 'extern' keyword properly

无人久伴 提交于 2021-02-06 10:52:30

问题


There are sources (books, online materials) that explain the usage of extern as following:

extern int i;        // declaration - has 'extern'
int i = 1;           // definition  - specified by the absence of 'extern'

And there are sources that support the following syntax:

extern int i;        // declaration
extern int i = 1;    // definition  - specified by the equal sign
                     // Both marked with 'extern'

My question is - is this a C vs. C++ distinction, or is it a pre-ANSI vs. ANSI practice?

Now, the more practical question:

Using the second syntax, I want to create a global object (visible from every compilation unit). The constructor takes no parameters, so neither parentheses, nor the equal sign are necessary.

extern MyClass myobject;

Now how can the compiler make the distinction between a declaration and the definition?

EDIT: Back at school, I was used to the first syntax (Borland C). Later I used a compiler (probably some ancient version of GCC) that refused to compile a definition without an 'extern'. That is what made me confused.


回答1:


Specifically for your examples, there's no distinction between C and C++ at work here. The basic rule that works in both languages is this: if your declaration includes an initializer, then it is a definition. Period. It does not matter, whether it has explicit extern in it or not. If it has an initializer, then it is a definition.

That means that in namespace scope both extern int i = 1 and int i = 1 are equivalent, i.e. extern in such declaration is redundant. In C++ extern in definitions becomes non-redundant when the declared object is const, since const objects in C++ have internal linkage by default. For example, extern const int c = 42; defines constant c with external linkage.

If a declaration has no initializer, then (and only then) it begins to depend on the presence of extern keyword. With extern it is a non-defining declaration. Without extern it is a definition. (In C it would be a tentative definition, but that's beside the point in our context).

Now, for your practical question. In order to create a global object, you have to declare it as

extern MyClass myobject;

(which will usually be done in a header file), and then define it in some translation unit as

MyClass myobject;

Since your constructor takes no arguments, this is the only way to define your object. (Starting from C++11 you can also use MyClass myobject{}; if you so desire.)

If you had to supply arguments to the constructor (say, 42), you would be able to use both

MyClass myobject(42);

and

extern MyClass myobject(42);

as definition, since presence of an initializer ensures that it is indeed interpreted as a definition.




回答2:


For file-scope variables, whether they are of class type or primitive type:

  • extern T t; with no initialiser is a declaration;
  • extern T t = expression; with an initialiser of whatever syntax (assignment, construction, or unified) is a definition;
  • T t; with no initialiser is a definition, initialised to the default value of T;
  • T t = expression; with an initialiser of whatever syntax is a definition.

There is no difference between extern int i = 1; and int i = 1;, and there are arguments to be made for both styles, but in general I'd argue for the second as you should already be aware that a definition at file scope has linkage.

Historically, it appears that pre-ANSI C the extern keyword was not required; see e.g. http://www.jetcafe.org/jim/c-style.html#Declarations

So for a class type, write extern MyClass myobject; for the declaration(s) and MyClass myobject; for the definition.




回答3:


draft n3337, 3.1.2

A declaration is a definition unless it declares a function without specifying the function’s body (8.4), it contains the extern specifier (7.1.1) or a linkage-specification25 (7.5) and neither an initializer nor a functionbody, it declares a static data member in a class definition (9.2, 9.4), it is a class name declaration (9.1), it is an opaque-enum-declaration (7.2), it is a template-parameter (14.1), it is a parameter-declaration (8.3.5) in a function declarator that is not the declarator of a function-definition, or it is a typedef declaration (7.1.3), an alias-declaration (7.1.3), a using-declaration (7.3.3), a static_assert-declaration (Clause 7), an attributedeclaration (Clause 7), an empty-declaration (Clause 7), or a using-directive (7.3.4). [ Example: all but one of the following are definitions:

int a; // defines a
extern const int c = 1; // defines c
int f(int x) { return x+a; } // defines f and defines x
struct S { int a; int b; }; // defines S, S::a, and S::b
struct X { // defines X
int x; // defines non-static data member x
static int y; // declares static data member y
X(): x(0) { } // defines a constructor of X
};
int X::y = 1; // defines X::y
enum { up, down }; // defines up and down
namespace N { int d; } // defines N and N::d
namespace N1 = N; // defines N1
X anX; // defines anX
whereas these are just declarations:
extern int a; // declares a
extern const int c; // declares c
int f(int); // declares f
struct S; // declares S
typedef int Int; // declares Int
extern X anotherX; // declares anotherX
using N::d; // declares d

—end example ]




回答4:


I want to create a global object (visible from every compilation unit)

extern is not necessary for the definition, because external linkage is the default.

What you should do is put:

extern MyClass myobject;

in a header file (this is not a definition) to make the compiler aware of the data type when compiling other compilation units. Then, in exactly one compilation unit, write:

MyClass myobject;

which is a definition with external linkage.




回答5:


Ok, so the extern keyword is placed before the name of a variable in an external file. So say you have a separate file in your Project. Say that file is a header file called MyMath.h (as if you were making a portable math file with some useful functions/classes). In your header file you would put all the prototypes, or forward references, for your cool math functions and classes. The actual code, or functions, etc. for this math header file would be in a .cpp file called MyMath.cpp (usually you use the same name to keep it all organized). The extern keyword comes into play here: if you wanted to have a global variable for PI (3.1415) in your math file, you need to define it (in the .cpp file) the same as usual, float PI = 3.1415; and THEN in your .h or header file you would write the prototype, or declaration of the variable, with the prefix extern.

So the full example could look like this:

----MyMath.h----

#ifndef MYMATH_H_INCLUDED
#define MYMATH_H_INCLUDED

extern float PI;

#endif // MYMATH_H_INCLUDED

----MyMath.cpp----

#include "MyMath.h"

float PI = 3.1415;

----main.cpp----

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

using namespace std;

int main()
{
    cout << "PI = " << PI << endl;
    return 0;
}

Hope I explained thoroughly! Remember, this is for variables' use between files!




回答6:


"initializer" beats "extern". Action beats talk.



来源:https://stackoverflow.com/questions/11867431/using-the-extern-keyword-properly

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!