有些来源(书籍,在线资料)解释了extern
的用法如下:
extern int i; // declaration - has 'extern'
int i = 1; // definition - specified by the absence of 'extern'
有些来源支持以下语法:
extern int i; // declaration
extern int i = 1; // definition - specified by the equal sign
// Both marked with 'extern'
我的问题是 - 这是 C 与 C ++ 的区别,还是 pre-ANSI 与 ANSI 练习?
现在,更实际的问题是:
使用第二种语法,我想创建一个全局对象(从每个编译单元可见)。构造函数不带参数,因此不需要括号,也不需要等号。
extern MyClass myobject;
现在,编译器如何区分声明和定义?
编辑:回到学校,我习惯了第一种语法(Borland C)。后来我使用了一个编译器(可能是GCC的一些古老版本)拒绝编译没有'extern'的定义。这让我很困惑。
答案 0 :(得分:9)
特别是对于您的示例,此处的C和C ++之间没有区别。适用于这两种语言的基本规则是:如果您的声明包含初始值设定项,则它是定义。期。无论是否有明确的extern
都没关系。如果它有初始值设定项,则它是定义。
这意味着在命名空间范围内,extern int i = 1
和int i = 1
都是等效的,即此类声明中的extern
是多余的。在C ++ extern
中,当声明的对象为const
时,定义变为非冗余,因为C ++中的const
对象默认具有内部链接。例如,extern const int c = 42;
使用外部链接定义常量c
。
如果声明没有初始值设定项,那么(并且只有这样)它才开始依赖于extern
关键字的存在。使用extern
,它是非定义声明。没有extern
,它就是定义。 (在C中它将是暂定的定义,但这与我们上下文中的点不同。)
现在,针对您的实际问题。要创建全局对象,您必须将声明为
extern MyClass myobject;
(通常在头文件中完成),然后在某些翻译单元中定义
MyClass myobject;
由于构造函数不带参数,因此这是定义对象的唯一方法。 (从C ++ 11开始,如果您愿意,也可以使用MyClass myobject{};
。)
如果你必须为构造函数提供参数(例如,42
),你就可以使用它们
MyClass myobject(42);
和
extern MyClass myobject(42);
作为定义,因为初始化程序的存在确保它确实被解释为定义。
答案 1 :(得分:4)
对于文件范围变量,无论它们是类类型还是基本类型:
extern T t;
是声明; extern T t = expression;
带有任何语法的初始化(赋值,构造或统一)是一个定义; T t;
是一个定义,初始化为默认值T
; T t = expression;
使用任何语法的初始化程序定义。 extern int i = 1;
和int i = 1;
之间没有区别,并且两种风格都有争论,但总的来说我会争论第二种,因为你应该已经知道了这个定义在文件范围内有链接。
从历史上看,似乎在ANSI C之前不需要extern
关键字;见例如http://www.jetcafe.org/jim/c-style.html#Declarations
因此,对于类类型,请为声明编写extern MyClass myobject;
,为定义编写MyClass myobject;
。
答案 2 :(得分:3)
草案n3337,3.1.2
声明是一个定义,除非声明没有的函数 指定函数的主体(8.4),它包含extern说明符 (7.1.1)或者链接规范25(7.5)并且都没有初始化程序 也不是函数体,它在类中声明了静态数据成员 定义(9.2,9.4),它是一个类名声明(9.1),它是一个 opaque-enum-declaration(7.2),它是一个模板参数(14.1),它 是函数声明符中的参数声明(8.3.5) 不是函数定义的声明者,或者是typedef 声明(7.1.3),别名声明(7.1.3),使用声明 (7.3.3),static_assert-declaration(第7条),a attributedeclaration(第7条),空声明(第7条)或a using-directive(7.3.4)。 [示例:以下所有内容均为 定义:
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]
答案 3 :(得分:2)
我想创建一个全局对象(从每个编译单元可见)
extern
对于定义不是必需的,因为外部链接是默认的。
你应该做的是:
extern MyClass myobject;
在头文件中(这不是定义)使编译器在编译其他编译单元时知道数据类型。然后,在一个编译单元中,写下:
MyClass myobject;
这是一个外部联系的定义。
答案 4 :(得分:2)
好的,所以extern
关键字放在外部文件中变量的名称之前。所以说你的项目中有一个单独的文件。假设该文件是名为MyMath.h的头文件(就好像您正在制作带有一些有用函数/类的可移植数学文件)。在您的头文件中,您可以为所有酷的数学函数和类放置所有原型或前向引用。此数学头文件的实际代码或函数等将位于名为MyMath.cpp的.cpp文件中(通常使用相同的名称将其全部组织起来)。 extern
关键字在这里发挥作用:如果你想在数学文件中有PI(3.1415)的全局变量,你需要像往常一样定义它(在.cpp文件中),{{1在你的.h或头文件中你可以编写原型或变量声明,前缀为float PI = 3.1415;
。
所以完整的例子可能如下所示:
---- ---- MyMath.h
extern
---- ---- MyMath.cpp
#ifndef MYMATH_H_INCLUDED
#define MYMATH_H_INCLUDED
extern float PI;
#endif // MYMATH_H_INCLUDED
---- ---- main.cpp中
#include "MyMath.h"
float PI = 3.1415;
希望我彻底解释!请记住,这是变量'在文件之间使用!
答案 5 :(得分:0)
“初始化程序”击败“extern”。行动节拍。