C++|多文件与分别编译及声明、定义,头文件可以包含的内容

To allow programs to be broken up into logical parts, C++ supports what is commonly known as separate compilation. Separate compilation lets us compose a program from several files.

为了允许把程序分成独立的逻辑块,C++ 支持所谓的分别编译。这样程序可以由多个文件组成。

In order for multiple files to access the same variable, C++ distinguishes between declarations and definitions.

为了让多个文件访问相同的变量,C++ 区分了声明和定义。

1 声明和定义

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 keyword extern:

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

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

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:

extern double pi = 3.1416; // definition

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 声明被当作是定义,所以该变量任何随后的定义都是错误的:

extern double pi = 3.1416; // definition
double pi;                 // error: redefinition of pi

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

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

extern double pi = 3.1416; // definition
extern double pi;          // ok: declaration not definition
extern double pi = 3.1416; // error: redefinition of pi

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.

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

2 强静态类型、类型检查

C++ is a statically typed language, which means that types are checked at compile time. The process by which types are checked is referred to as type-checking.

C++ 是一门静态类型语言,在编译时会作类型检查。

Most modern programming languages supplement this basic set of features in two ways: They let programmers extend the language by defining their own data types, and they provide a set of library routines that define useful functions and data types not otherwise built into the language.

大多数现代程序语言都采用两种方式扩充上述基本特征集:允许程序员通过自定义数据类型扩展该语言;提供一组库例程,这些例程定义了一些并非内置在语言中的实用函数和数据类型。

In C++, as in most programming languages, the type of an object determines what operations can be performed on it. Depending on the type of the objects involved, a statement might or might not be legal. Some languages, notably Smalltalk and Python, check the types involved in expressions at run time. In contrast, C++ is a statically typed language; type-checking is done at compile time. As a consequence, the compiler must be told the type of every name used in the program before that name can be used.

和大多数程序设计语言一样,C++ 中对象的类型决定了该对象可以执行的操作。语句正确与否取决于该语句中对象的类型。一些程序设计语言,特别是 Smalltalk 和 Python,在运行时才检查语句中对象的类型。相反,C++ 是静态类型(statically typed)语言,在编译时执行类型检查。结果是程序中使用某个名字之前,必须先告知编译器该名字的类型。

In most languages, the type of an object constrains the operations that the object can perform. If the type does not support a given operation, then an object of that type cannot perform that operation.

在大多数语言中,对象的类型限制了对象可以执行的操作。如果某种类型不支持某种操作,那么这种类型的对象也就不能执行该操作。

A variable provides us with named storage that our programs can manipulate. Each variable in C++ has a specific type, which determines the size and layout of the variable's memory; the range of values that can be stored within that memory; and the set of operations that can be applied to the variable. C++ programmers tend to refer to variables as "variables" or as "objects" interchangeably.

变量提供了程序可以操作的有名字的存储区。C++ 中的每一个变量都有特定的类型,该类型决定了变量的内存大小和布局、能够存储于该内存中的值的取值范围以及可应用在该变量上的操作集。C++ 程序员常常把变量称为“变量”或“对象(object)”。

In C++, whether an operation is legal or not is checked at compile time. When we write an expression, the compiler checks that the objects used in the expression are used in ways that are defined by the type of the objects. If not, the compiler generates an error message; an executable file is not produced.

在 C++ 中,操作是否合法是在编译时检查的。当编写表达式时,编译器检查表达式中的对象是否按该对象的类型定义的使用方式使用。如果不是的话,那么编译器会提示错误,而不产生可执行文件。

As our programs, and the types we use, get more complicated, we'll see that static type checking helps find bugs in our programs earlier. A consequence of static checking is that the type of every entity used in our programs must be known to the compiler. Hence, we must define the type of a variable before we can use that variable in our programs.

随着程序和使用的类型变得越来越复杂,我们将看到静态类型检查能帮助我们更早地发现错误。静态类型检查使得编译器必须能识别程序中的每个实体的类型。因此,程序中使用变量前必须先定义变量的类型

3 header(头文件)

Header is a mechanism for making class definitions and other declarations available in multiple source files.

头文件是使得类的定义和其他声明在多个源文件中可见的一种机制。

A header provides a centralized location for related declarations. Headers normally contain class definitions, extern variable declarations, and function declarations.

头文件为相关声明提供了一个集中存放的位置。头文件一般包含类的定义、extern 变量的声明和函数的声明。

Proper use of header files can provide two benefits: All files are guaranteed to use the same declaration for a given entity; and should a declaration require change, only the header needs to be updated.

头文件的正确使用能够带来两个好处:保证所有文件使用给定实体的同一声明;当声明需要修改时,只有头文件需要更新。

Headers Are for Declarations, Not Definitions.

头文件用于声明而不是用于定义。

When designing a header it is essential to remember the difference between definitions, which may only occur once, and declarations, which may occur multiple times. The following statements are definitions and therefore should not appear in a header:

当设计头文件时,记住定义和声明的区别是很重要的。定义只可以出现一次,而声明则可以出现多次。下列语句是一些定义,所以不应该放在头文件里:

extern int ival = 10;      // initializer, so it's a definition
double fica_rate;          // no extern, so it's a definition

Although ival is declared extern, it has an initializer, which means this statement is a definition. Similarly, the declaration of fica_rate, although it does not have an initializer, is a definition because the extern keyword is absent. Including either of these definitions in two or more files of the same program will result in a linker error complaining about multiple definitions.

虽然 ival 声明为 extern,但是它有初始化式,代表这条语句是一个定义。类似地,fica_rate 的声明虽然没有初始化式,但也是一个定义,因为没有关键字 extern。同一个程序中有两个以上文件含有上述任一个定义都会导致多重定义链接错误。

There are three exceptions to the rule that headers should not contain definitions: classes, const objects whose value is known at compile time, and inline functions covers inline functions) are all defined in headers. These entities may be defined in more than one source file as long as the definitions in each file are exactly the same.

对于头文件不应该含有定义这一规则,有三个例外。头文件可以定义类、值在编译时就已知道的 const 对象和 inline 函数。这些实体可在多个源文件中定义,只要每个源文件中的定义是相同的。

These entities are defined in headers because the compiler needs their definitions (not just declarations) to generate code. For example, to generate code that defines or uses objects of a class type, the compiler needs to know what data members make up that type. It also needs to know what operations can be performed on these objects. The class definition provides the needed information. That const objects are defined in a header may require a bit more explanation.

在头文件中定义这些实体,是因为编译器需要它们的定义(不只是声明)来产生代码。例如:为了产生能定义或使用类的对象的代码,编译器需要知道组成该类型的数据成员。同样还需要知道能够在这些对象上执行的操作。类定义提供所需要的信息。在头文件中定义 const 对象则需要更多的解释(见下面的第4节)。

在头文件中定义inline函数,这类函数是在调用处做代码展开的,所以也不存在重复定义的情况。

It is important to design header files so that they can be included more than once in a single source file. We must ensure that including a header file more than once does not cause multiple definitions of the classes and objects that the header file defines. A common way to make headers safe uses the preprocessor to define a header guard. The guard is used to avoid reprocessing the contents of a header file if the header has already been seen.

设计头文件时,应使其可以多次包含在同一源文件中,这一点很重要。我们必须保证多次包含同一头文件不会引起该头文件定义的类和对象被多次定义。使得头文件安全的通用做法,是使用预处理器定义头文件保护符。头文件保护符用于避免在已经见到头文件的情况下重新处理该头文件的内容。

We can use these facilities to guard against including a header more than once:

可以使用这些设施来预防多次包含同一头文件:

     #ifndef SALESITEM_H
     #define SALESITEM_H
     // Definition of Sales_itemclass and related functions goes here
     #endif

4 const修饰的变量定义为什么可以放到头文件中

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

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

However, there can be only one definition 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++ 中的任何变量都只能定义一次。定义会分配存储空间,而所有对该变量的使用都关联到同一存储空间。因为 const 对象默认为定义它的文件的局部变量,所以把它们的定义放在头文件中是合法的。

There is one important implication of this behavior. When we define a const in a header file, every source file that includes that header has its own const 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 such const variables by their corresponding constant expression at compile time. So, in practice, there won't be any storage used to hold const 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, the const should be defined and initialized in a source file. An extern declaration for that const should be made in the header, enabling multiple files to share that variable.

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

Types can be nonconst or const; a const object must be initialized and its value may not be changed. In addition, we can define compound types, such as references. A reference provides another name for an object. A compound type is a type that is defined in terms of another type.

类型可以为 const 或非 const;const 对象必须要初始化,且其值不能被修改。

5 相关术语

separate compilation(分别编译)

Ability to split a program into multiple separate source files.

将程序分成多个分离的源文件进行编译。

type-checking(类型检查)

Term used to describe the process by which the compiler verifies that the way objects of a given type are used is consistent with the definition of that type.

编译器验证给定类型的对象的使用方式是否与该类型的定义一致,描述这一过程的术语。

declaration(声明)

Asserts the existence of a variable, function, or type defined elsewhere in the program. Some declarations are also definitions; only definitions allocate storage for variables. A variable may be declared by preceeding its type with the keyword extern. Names may not be used until they are defined or declared.

表明在程序中其他地方定义的变量、函数或类型的存在性。有些声明也是定义。只有定义才为变量分配存储空间。可以通过在类型前添加关键字 extern 来声明变量。名字直到定义或声明后才能使用。

definition(定义)

Allocates storage for a variable of a specified type and optionally initializes the variable. Names may not be used until they are defined or declared.

为指定类型的变量分配存储空间,也可能可选地初始化该变量。名字直到定义或声明后才能使用。

header(头文件)

A mechanism for making class definitions and other declarations available in multiple source files. User-defined headers are stored as files. System headers may be stored as files or in some other system-specific format.

使得类的定义和其他声明在多个源文件中可见的一种机制。用户定义的头文件以文件方式保存。系统头文件可能以文件方式保存,也可能以系统特有的其他格式保存。

header guard(头文件保护符)

The preprocessor variable defined to prevent a header from being included more than once in a single source file.

为防止头文件被同一源文件多次包含而定义的预处理器变量。

implementation(实现)

The (usually private) members of a class that define the data and any operations that are not intended for use by code that uses the type. The istream and ostream classes, for example, manage an IO buffer that is part of their implementation and not directly accessible to users of those classes.

定义数据和操作的类成员(通常为 private),这些数据和操作并非为使用该类型的代码所用。例如,istream 和 ostream 类管理的 IO 缓冲区是它们的实现的一部分,但并不允许这些类的使用者直接访问。

variable initialization(变量初始化)

Term used to describe the rules for initializing variables and array elements when no explicit initializer is given. For class types, objects are initialized by running the class's default constructor. If there is no default constructor, then there is a compile-time error: The object must be given an explicit initializer. For built-in types, initialization depends on scope. Objects defined at global scope are initialized to 0; those defined at local scope are uninitialized and have undefined values.

描述当没有给出显式初始化式时初始化变量或数组元素的规则的术语。对类类型来说,通过运行类的默认构造函数来初始化对象。如果没有默认构造函数,那么将会出现编译时错误:必须要给对象指定显式的初始化式。对于内置类型来说,初始化取决于作用域。定义在全局作用域的对象初始化为 0,而定义在局部作用域的对象则未初始化,拥有未定义值。

ref:

C++ primer / Stanley B. Lippman, Josée Lajoie, Barbara E. Moo

-End-

展开阅读全文

页面更新:2024-04-20

标签:定义   声明   文件   常量   编译器   源文件   表达式   初始化   变量   函数   存储空间   对象   类型   操作   程序

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top