《C++Primer》学习疑惑1-----const头文件定义问题

来源:互联网 发布:matlab trapz函数算法 编辑:程序博客网 时间:2024/05/16 05:45

对“因为 const 对象默认为定义它的文件的局部变量,所以把它们的定义放在头文件中是合法的。”这句不是很理解,查了查资料和其他人的博客和论坛,整理一下(最后附有《C++Primer》原文):

1.理解这里所说的“局部变量”。

原文:在全局作用域里定义非const 变量时,它在整个程序中都可以访问。我们可以把一个非 const 变更定义在一个文件中,假设已经做了合适的声明,就可在另外的文件中使用这个变量。

与其他变量不同,除非特别说明,在全局作用域声明的 const 变量是定义该对象的文件的局部变量。此变量只存在于那个文件中,不能被其他文件访问。要使 const 变量能够在其他的文件中访问,必须地指定它为extern

比如头文件 cost.h 中写了const int CON=1; 在main.cpp里面写了
#include<iostream>
#include"const.h"
using namespace std;
{
cout << CON;
return 0;
}

int main()

在 C++(但不是在 C 语言)中,const 限定符对默认存储类型稍有影响。在默认情况下,全局变量的链接性为外部的,但 const 全局变量的链接性为内部的。也就是说,在 C++ 看来,全局 const 定义就像使用了 static 说明符一样。因此,可以将 const 常量定义在头文件中供工程中的多个其它文件包含引用,并且编译时不会产生变量重复定义的错误。当然,也可以用 #define 宏定义。


此变量只存在于那个文件中,不能被其他文件访问。也就是说,CON这个变量只存在于const.h中,不能被其他文件(例如**.cpp)访问。

“局部变量”更准确地说,是编译单元的局部(可见)变量,所谓的编译单元就是指.cpp,就是说这个局部变量属于这个编译单元。并不属于哪个函数,默认就是內链属性


2.理解“在一个程序中只能定义一次,可以声明多次

原文:C++ 程序通常由许多文件组成。为了让多个文件访问相同的变量,C++ 区分了声明和定义。

变量的定义用于为变量分配存储空间,还可以为变量指定初始值。在一个程序中,变量有且仅有一个定义。


声明用于向程序表明变量的类型和名字。定义也是声明:当定义变量时我们声明了它的类型和名字。可以通过使用extern关键字声明变量名而不定义它。不定义变量的声明包括对象名、对象类型和对象类型前的关键字extern:

extern int i; // declares but does not define i

int i; // declares and defines i extern double pi = 3.1416; // definition

只有当声明也是定义时,声明才可以有初始化式,因为只有定义才分配存储空间。初始化式必须要有存储空间来进行初始化。如果声明有初始化式,那么它可被当作是定义,即使声明标记为extern

任何在多个文件中使用的变量都需要有与定义分离的声明。在这种情况下,一个文件含有变量的定义,使用该变量的其他文件则包含该变量的声明(而不是定义)。


3.理解“放在头文件中是合法的”

#include是简单的文本替换

就是每个写有#include"const.h"的**.cpp都有了这样一个语句:const int CON=1; 意味着每个编译单元(**.cpp)都可以有一个自己的const变量CON这里const的作用是:

  • 如果另外有一个main1.cpp也#include"const.h",那么在main1.cpp里面也有一个CON,
因为:在全局作用域声明的 const 变量是定义该对象的文件的局部变量。此变量只存在于那个文件中,不能被其他文件访问。所以:这个CON跟main.cpp里面的CON不是同一个(但它们有相同的名字和值)它们之间没有冲突,不会相互干扰
  • 如果CON前面不加const,那么如果有两个cpp同时#include"myheader.h",那么连接时就会出问题
因为:不加const 就是int CON=1; 在全局作用域里定义非const 变量时,它在整个程序中都可以访问。也就是在整个程序中都可以访问int CON=1;那么如果有两个cpp同时#include"myheader.h",两个**.cpp都有了这样一个语句: int CON=1; 而这是变量CON又整个程序可访问,也就是重复定义了。
const这个性质恰恰是为了“能用不冲突”而不是“看不见用不着”所以这样就可以在头文件中定义而不会出现重复定义的错误了.const对象默认的链接属性是internel。意味着每个编译单元(.cpp)都可以有一个自己的const。这就是为什么你可以将const对象放头文件的原因。如果你在前面加个extern然后在多个.cpp文件里面包含该头文件后再编译试试。

关于linkage

A name is said to have linkage when it might denote the same object, reference, function, type, template,
namespace or value as a name introduced by a declaration in another scope:
other translation units or from other scopes of the same translation unit.
— When a name has internal linkage, the entity it denotes can be referred to by names from other scopes in
the same translation unit.
— When a name has no linkage, the entity it denotes cannot be referred to by names from other scopes.

— When a name has external linkage, the entity it denotes can be referred to by names from scopes of

关于全局scope

The outermost declarative region of a translation unit is also a namespace, called the global namespace. A
name declared in the global namespace has global namespace scope (also called global scope). The potential
scope of such a name begins at its point of declaration (3.3.1) and ends at the end of the translation unit
that is its declarative region. Names with global namespace scope are said to be global.

关于namespace scope和linkage

A name having namespace scope (3.3.5) has internal linkage if it is the name of
— an object, reference, function or function template that is explicitly declared 
— an object or reference that is explicitly declared const and neither explicitly declared extern nor
previously declared to have external linkage; or
— a data member of an anonymous union.
4 A name having namespace scope has external linkage if it is the name of
— an object or reference, unless it has internal linkage; or
— a function, unless it has internal linkage; or
— a named class (clause 9), or an unnamed class defined in a typedef declaration in which the class has the
typedef name for linkage purposes (7.1.3); or
— a named enumeration (7.2), or an unnamed enumeration defined in a typedef declaration in which the
enumeration has the typedef name for linkage purposes (7.1.3); or
— an enumerator belonging to an enumeration with external linkage; or
— a template, unless it is a function template that has internal linkage (clause 14); or
— a namespace (7.3), unless it is declared within an unnamed namespace.

问题3书中原文:

Some const Objects Are Defined in Headers
一些 const 对象定义在头文件中

Recall that by default a const variable (Section 2.4, p.57) is local to the file in which it is defined. As we shall now see, the reason for this default is to allowconst variables to be defined in header files.

回想一下,const 变量(第 2.4 节)默认时是定义该变量的文件的局部变量。正如我们现在所看到的,这样设置默认情况的原因在于允许const 变量定义在头文件中。

In C++ there are places where constant expression (Section 2.7, p.62) is required. For example, the initializer of an enumerator must be a constant expression. We'll see other cases that require constant expressions in later chapters.

在 C++ 中,有些地方需要放置常量表达式(第 2.7 节)。例如,枚举成员的初始化式必须是常量表达式。在以后的章节中将会看到其他需要常量表达式的例子。

Generally speaking, a constant expression is an expression that the compiler can evaluate at compile-time. Aconst variable of integral type may be a constant expression when it is itself initialized from a constant expression. However, for theconst to be a constant expression, the initializer must be visible to the compiler. To allow multiple files to use the same constant value, theconst and its initializer must be visible in each file. To make the initializer visible, we normally define suchconsts inside a header file. That way the compiler can see the initializer whenever theconst is used.

一般来说,常量表达式是编译器在编译时就能够计算出结果的表达式。当 const 整型变量通过常量表达式自我初始化时,这个const 整型变量就可能是常量表达式。而const 变量要成为常量表达式,初始化式必须为编译器可见。为了能够让多个文件使用相同的常量值,const 变量和它的初始化式必须是每个文件都可见的。而要使初始化式可见,一般都把这样的const 变量定义在头文件中。那样的话,无论该const 变量何时使用,编译器都能够看见其初始化式。

However, there can be only one definition (Section 2.3.5, p.52) for any variable in a C++ program. A definition allocates storage; all uses of the variable must refer to the same storage. Because, by default,const objects are local to the file in which they are defined, it is legal to put their definition in a header file.

但是,C++ 中的任何变量都只能定义一次(第 2.3.5 节)。定义会分配存储空间,而所有对该变量的使用都关联到同一存储空间。因为const 对象默认为定义它的文件的局部变量,所以把它们的定义放在头文件中是合法的。

There is one important implication of this behavior. When we define aconst in a header file, every source file that includes that header has its ownconst variable with the same name and value.

这种行为有一个很重要的含义:当我们在头文件中定义了 const 变量后,每个包含该头文件的源文件都有了自己的const 变量,其名称和值都一样。

When the const is initialized by a constant expression, then we are guaranteed that all the variables will have the same value. Moreover, in practice, most compilers will replace any use of suchconst variables by their corresponding constant expression at compile time. So, in practice, there won't be any storage used to holdconst variables that are initialized by constant expressions.

当该 const 变量是用常量表达式初始化时,可以保证所有的变量都有相同的值。但是在实践中,大部分的编译器在编译时都会用相应的常量表达式替换这些const 变量的任何使用。所以,在实践中不会有任何存储空间用于存储用常量表达式初始化的const 变量。

When a const is initialized by a value that is not a constant expression, then it should not be defined in header file. Instead, as with any other variable, theconst should be defined and initialized in a source file. Anextern declaration for thatconst should be made in the header, enabling multiple files to share that variable.

如果 const 变量不是用常量表达式初始化,那么它就不应该在头文件中定义。相反,和其他的变量一样,该 const 变量应该在一个源文件中定义并初始化。应在头文件中为它添加 extern 声明,以使其能被多个文件共享。


问题1(“const 变量默认时是定义该变量的文件的局部变量。”中是局部变量问题)书中原文:

const Objects Are Local to a File By Default
const 对象默认为文件的局部变量
// file_1.cc
int counter; // definition
// file_2.cc
extern int counter; // uses counter from file_1
++counter; // increments counter defined in file_1
// file_1.cc
// defines and initializes a const that is accessible to other files
extern const int bufSize = fcn();
// file_2.cc
extern const int bufSize; // uses bufSize from file_1
// uses bufSizedefined infile_1
for (int index = 0; index != bufSize; ++index)
// ...

Nonconst variables are extern by default. To make aconst variable accessible to other files we must explicitly specify that it isextern.

const 变量默认为 extern。要使 const 变量能够在其他的文件中访问,必须地指定它为extern

When we define a nonconst variable at global scope (Section 2.3.6, p.54), it is accessible throughout the program. We can define a nonconst variable in one file andassuming an appropriate declaration has been madecan use that variable in another file:

在全局作用域(第 2.3.6 节)里定义非const 变量时,它在整个程序中都可以访问。我们可以把一个非const 变更定义在一个文件中,假设已经做了合适的声明,就可在另外的文件中使用这个变量:


Unlike other variables, unless otherwise specified, const variables declared at global scope are local to the file in which the object is defined. The variable exists in that file only and cannot be accessed by other files.

与其他变量不同,除非特别说明,在全局作用域声明的 const 变量是定义该对象的文件的局部变量。此变量只存在于那个文件中,不能被其他文件访问。

We can make a const object accessible throughout the program by specifying that it isextern:

通过指定 const 变更为 extern,就可以在整个程序中访问 const 对象:


In this program, file_1.cc defines and initializes bufSize to the result returned from calling the function named fcn. The definition ofbufSize isextern, meaning thatbufSize can be used in other files. The declaration infile_2.cc is also madeextern. In this case, theextern signifies thatbufSize is a declaration and hence no initializer is provided.

本程序中,file_1.cc 通过函数 fcn 的返回值来定义和初始化 bufSize。而bufSize 定义为extern,也就意味着bufSize 可以在其他的文件中使用。file_2.ccextern 的声明同样是extern;这种情况下,extern 标志着bufSize 是一个声明,所以没有初始化式。

We'll see in Section 2.9.1 (p.69) whyconst objects are made local to a file.

我们将会在第 2.9.1 节看到为何const 对象局部于文件创建。

问题2原文:

2.3.5. Declarations and Definitions
2.3.5. 声明和定义
extern int i; // declares but does not definei
int i; // declares and defines i
extern double pi = 3.1416; // definition
extern double pi = 3.1416; // definition
double pi; // error: redefinition of pi
extern double pi = 3.1416; // definition
extern double pi; // ok: declaration not definition
extern double pi = 3.1416; // error: redefinition ofpi

In C++ a variable must be defined exactly once and must be defined or declared before it is used.

在 C++ 语言中,变量必须且仅能定义一次,而且在使用变量之前必须定义或声明变量。

As we'll see in Section 2.9 (p.67), C++ programs typically are composed of many files. In order for multiple files to access the same variable, C++ distinguishes between declarations and definitions.

正如将在第 2.9 节所看到的那样,C++ 程序通常由许多文件组成。为了让多个文件访问相同的变量,C++ 区分了声明和定义。

A definition of a variable allocates storage for the variable and may also specify an initial value for the variable. There must be one and only one definition of a variable in a program.

变量的定义用于为变量分配存储空间,还可以为变量指定初始值。在一个程序中,变量有且仅有一个定义。

A declaration makes known the type and name of the variable to the program. A definition is also a declaration: When we define a variable, we declare its name and type. We can declare a name without defining it by using the extern keyword. A declaration that is not also a definition consists of the object's name and its type preceded by the keywordextern:

声明用于向程序表明变量的类型和名字。定义也是声明:当定义变量时我们声明了它的类型和名字。可以通过使用extern关键字声明变量名而不定义它。不定义变量的声明包括对象名、对象类型和对象类型前的关键字extern:

An extern declaration is not a definition and does not allocate storage. In effect, it claims that a definition of the variable exists elsewhere in the program. A variable can be declared multiple times in a program, but it must be defined only once.

extern 声明不是定义,也分配存储空间。事实上,它只是说明变量定义在程序的其他地方。程序中变量可以声明多次,但只能定义一次。

A declaration may have an initializer only if it is also a definition because only a definition allocates storage. The initializer must have storage to initialize. If an initializer is present, the declaration is treated as a definition even if the declaration is labeled extern:

只有当声明也是定义时,声明才可以有初始化式,因为只有定义才分配存储空间。初始化式必须要有存储空间来进行初始化。如果声明有初始化式,那么它可被当作是定义,即使声明标记为extern

Despite the use of extern, this statement defines pi. Storage is allocated and initialized. An extern declaration may include an initializer only if it appears outside a function.

虽然使用了 extern ,但是这条语句还是定义了 pi,分配并初始化了存储空间。只有当extern 声明位于函数外部时,才可以含有初始化式。

Because an extern that is initialized is treated as a definition, any subseqent definition of that variable is an error:

因为已初始化的 extern 声明被当作是定义,所以该变量任何随后的定义都是错误的:

Similarly, a subsequent extern declaration that has an initializer is also an error:

同样,随后的含有初始化式的 extern 声明也是错误的:

The distinction between a declaration and a definition may seem pedantic but in fact is quite important.

声明和定义之间的区别可能看起来微不足道,但事实上却是举足轻重的。

Any variable that is used in more than one file requires declarations that are separate from the variable's definition. In such cases, one file will contain the definition for the variable. Other files that use that same variable will contain declarations forbut not a definition ofthat same variable.

任何在多个文件中使用的变量都需要有与定义分离的声明。在这种情况下,一个文件含有变量的定义,使用该变量的其他文件则包含该变量的声明(而不是定义)。



0 0