1. 程式人生 > >《C++Primer》學習疑惑1-----const標頭檔案定義問題

《C++Primer》學習疑惑1-----const標頭檔案定義問題

對“因為 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 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 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.

任何在多個檔案中使用的變數都需要有與定義分離的宣告。在這種情況下,一個檔案含有變數的定義,使用該變數的其他檔案則包含該變數的宣告(而不是定義)。



相關推薦

C++Primer學習疑惑1-----const檔案定義問題

對“因為 const 物件預設為定義它的檔案的區域性變數,所以把它們的定義放在標頭檔案中是合法的。”這句不是很理解,查了查資料和其他人的部落格和論壇,整理一下(最後附有《C++Primer》原文): 1.理解這裡所說的“區域性變數”。 原文:在全域性作用域裡定義非cons

C++從零開始區塊鏈:P2P模組之公共檔案定義

搞了臺阿里雲做內網打洞測試,巨集開關ALITEST用來內外網測試轉換 #define SERVERIP “xx.xx.xx.xx” 是外網測試機的外網IP #include <cstdio> #include <cstdlib> #include <cst

C++ algorithm 檔案 定義的 sort() 實現絕對值排序

Problem Description 輸入n(n<=100)個整數,按照絕對值從大到小排序後輸出。題目保證對於每一個測試例項,所有的數的絕對值都不相等。 Input 輸入資料有多組,每組佔一行,每行的第一個數字為n,接著是n個整數,n=0表示輸入資料的結束,不做處理。

C語言中自帶的檔案(.h)所包含的函式

由於之前沒有好好學習過C語言,所以對其自帶標頭檔案所包含的內容總是不清楚,每次寫程式碼都是盲目的#include很多.h,現在重新整理一下,發現了不少很好的函式,以方便複習查閱。 不完全統計,C語言標

C/C++筆試必須熟悉掌握的檔案系列(二)——math.h/cmath

1. 說明   “math.h”是C語言中數學函式庫,包含我們常用的一些數學計算上會使用到的函式。C++中有對應相同作用的標頭檔案“cmath”,當然C++中兩個標頭檔案都可以使用,C++向C相容。

例項理解c++中向前宣告與引用檔案的區別

使用C++程式設計,編譯時經常出現這種錯誤"error: invalid use of incomplete type ‘class xxx’",或“error: forward declaration of ‘class xxx’”. 解決這種錯誤就是用理解c++中向前宣告與引用標頭檔案的

C語言檔案定義全域性變數問題

正確的作法是在c原始檔中定義一個全域性變數,在標頭檔案中加入全域性變數的宣告,在外部檔案呼叫的時候,包含其標頭檔案,加入全域性變數的宣告(不加也是可以的,最好加上)。 //1.c int a;

c/c++ 檔案 定義

對於大家,肯定會經常見到如是這樣的程式碼#ifndef LOG_H #define LOG_H #endif ********************************************************************************我記

c++:一個程式多個源/檔案

1、一個程式,一個原始檔的做法#include<iostream> #include<cstring> using namespace std; class Student { private: char Name[20

Jni程式設計(二)jni.h 檔案定義分析,以及c/c++呼叫java類的屬性和方法

在第一篇部落格中 我們初步瞭解了jni程式設計的步驟,那接下來我認為極其重要的事情是搞清楚jni.h標頭檔案裡面的結構,包括資料型別和方法的定義等,這些是必須的,否則沒有辦法進行學習,就像寫文章一樣,要先學會寫字是一樣的道理。 首先來看一下jni.h標頭檔案的組成:ps下面

C++】C語言標準庫以及標準檔案

靜態連結庫(Static Link Library)——   Linux 下的 .a 和 Windows 下的 .lib。 ANSI C 標準共定義了 15 個頭檔案,稱為“C標準庫”,所有的編譯器都必須支援,如何正確並熟練的使用這些標準庫,可以反映出一個程式

c++中兩個類的檔案互相包含編譯出錯的解決辦法

首先我們需要問一個問題是:為什麼兩個類不能互相包含標頭檔案? 所謂互相包含標頭檔案,我舉一個例子:我實現了兩個類:圖層類CLayer和符號類CSymbol,它們的大致關係是圖層裡包含有符號,符號裡定義一個相關圖層指標,具體請參考如下程式碼(注:以下程式碼僅供說明問題,不作為

C++primer(第五版)Sales_item.h檔案

C++primer(第五版)1.51練習章節需要有一個Sales_item類,但是給的網站有點蛋疼,直接複製下面就好咯: #ifndef SALESITEM_H #define SALESITEM_H #include <iostream> #include <string

C++菜鳥學習筆記系列(6)——簡單檔案的編寫

C++菜鳥學習筆記系列(6) ——簡單標頭檔案的編寫 我們在上一篇部落格 C++菜鳥學習筆記系列(5)中已經敘述了一些關於在C++中建立自己的資料型別的一些方法,但是隨之而來的一個問題是我們在建立了一個自定義類之後經常還要在其他的檔案中使用同樣的類,這時候我們可

C++學習筆記 — 理解檔案(.h)和原始檔(.cpp)

原始檔根據#include來關聯檔案 系統自帶的檔案用尖括號括起來,編譯器會在系統檔案目錄下查詢 #include <> 使用者自定義的檔案用雙括號括起來,編譯器首先在使用者目錄下查詢,然

C++Primer學習筆記_13.1 拷貝、賦值與銷燬

C++primer學習筆記——第13章拷貝控制 13.1拷貝、賦值與銷燬 13.1.1拷貝建構函式 定義: 建構函式第一個引數是自身型別的引用。 任何額外引數都有預設值 合成拷貝建構函式: 如果沒有自定義拷貝建構

c++primer學習日記0——引用、指標及const

·引用必須被初始化,且初始值必須是一個物件,像 int &i = 10 這就是錯誤的。 ·必須同類型引用,double a = 3.14; int &b = a;這是錯誤的。 ·指標只能存地址,不能把int變數賦值給指標 ·初始化所有指標 ·void*

C語言程式設計 學習筆記 12.3 多個原始碼檔案檔案、宣告

我們經常在做“分而治之”的事情(多個.c檔案): 1.main()裡的程式碼太長了適合分成幾個函式 2.一個原始碼檔案太長了適合分成幾個檔案 3.兩個獨立的原始碼檔案不能編譯成可執行的程式 對於(1),我們可以舉以下例子: 有個主函式main.c,有另外一個函式

Android學習筆記——NDK中C++標準庫、STL的配置;Include其他檔案

以下所有是基於Eclipse的,使用命令列的繞道。 1、STL的使用,以stlport為例官方的說法是隻需要在Application.mk檔案中新增如下一行即可 Application.mk程式碼   APP_STL := stlport_static   即可,可是

[C++ primer學習筆記] 3.2.1 定義和初始化string物件

類可以定義多種初始化物件的方式:或初始值的數量不同; 或初始值的型別不同。初始化的不同方式:拷貝初始化:使用等號(=)初始化變數,實際上執行的是拷貝初始化,編譯器把等號右側的初始值拷貝到新建立的物件中去直接初始化:不使用等號,則執行的是直接初始化當初始值只有一個時,使用直接/