欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

[c++]變量聲明與定義的規(guī)則詳解

 更新時(shí)間:2021年04月19日 17:28:26   作者:TOMOCAT  
這篇文章主要介紹了[c++]變量聲明與定義的規(guī)則詳解,對(duì)于學(xué)習(xí)c++的朋友來(lái)說(shuō)這是一個(gè)很細(xì)膩的文章,代碼完整,需要的朋友可以參考下

聲明與定義分離

Tips:變量能且僅能被定義一次,但是可以被多次聲明。

為了支持分離式編譯,C++將定義和聲明區(qū)分開(kāi)。其中聲明規(guī)定了變量的類(lèi)型和名字,定義除此功能外還會(huì)申請(qǐng)存儲(chǔ)空間并可能為變量賦一個(gè)初始值。

extern

如果想聲明一個(gè)變量而非定義它,就使用關(guān)鍵字extern并且不要顯式地初始化變量:

extern int i;      // 聲明i而非定義i
extern int i = 1;  // 定義i, 這樣做抵消了extern的作用 

static

當(dāng)我們?cè)贑/C++用static修飾變量或函數(shù)時(shí),主要有三種用途:

  • 局部靜態(tài)變量
  • 外部靜態(tài)變量/函數(shù)
  • 類(lèi)內(nèi)靜態(tài)數(shù)據(jù)成員/成員函數(shù)

其中第三種只有C++中有,我們后續(xù)在面向?qū)ο蟪绦蛟O(shè)計(jì)中再探討,這里只討論靜態(tài)局部/全局變量。

1. 靜態(tài)局部變量

在局部變量前面加上static說(shuō)明符就構(gòu)成靜態(tài)局部變量,例如:

// 聲明局部靜態(tài)變量
static int a;
static int array[5] = {1, 2, 3, 4, 5};
  • 靜態(tài)局部變量在函數(shù)內(nèi)定義,但不像自動(dòng)變量那樣當(dāng)函數(shù)被調(diào)用時(shí)就存在,調(diào)用結(jié)束就消失,靜態(tài)變量的生存期為整個(gè)源程序
  • 靜態(tài)變量的生存期雖然為整個(gè)源程序,但是作用域與自動(dòng)變量相同,即只能在定義該變量的函數(shù)內(nèi)使用該變量,退出函數(shù)后雖然變量還存在,但不能夠使用它
  • 對(duì)基本類(lèi)型的靜態(tài)局部變量如果在聲明時(shí)未賦初始值,則系統(tǒng)自動(dòng)賦0值;而對(duì)普通局部變量不賦初始值,那么它的值是不確定的

根據(jù)靜態(tài)局部變量的特點(diǎn),它的生存期為整個(gè)源程序,在離開(kāi)定義它的函數(shù)(作用域)但再次調(diào)用定義它的函數(shù)時(shí),它又可繼續(xù)使用,而且保存了前次被調(diào)用后留下的值。因此,當(dāng)多次調(diào)用一個(gè)函數(shù)且要求在調(diào)用之間保留某些變量的值時(shí),可考慮采用靜態(tài)局部變量,雖然用全局變量也可以達(dá)到上述目的,但全局變量有時(shí)會(huì)造成意外的副作用,因此最好采用局部靜態(tài)變量。

例如:

#include <iostream>
void foo() {
    int j = 0;         // 普通局部變量
    static int k = 0;  // 靜態(tài)局部變量
    ++j;
    ++k;
    printf("j:%d, k:%d\n", j, k);
}
int main(void)
{
    for (int i = 1; i <= 5; i++) {
        foo();
    }
}
// 輸出:
j:1, k:1
j:1, k:2
j:1, k:3
j:1, k:4
j:1, k:5

2. 靜態(tài)全局變量(C++廢棄,用匿名命名空間替代)

Tips:對(duì)于全局變量,不管是否被static修飾,它的存儲(chǔ)區(qū)域都是在靜態(tài)存儲(chǔ)區(qū),生存期為整個(gè)源程序。只不過(guò)加上static后限制這個(gè)全局變量的作用域只能在定義該變量的源文件內(nèi)。

全局變量(外部變量)的聲明之前加上static就構(gòu)成了靜態(tài)的全局變量,全局變量本身就是靜態(tài)存儲(chǔ)變量,靜態(tài)全局變量當(dāng)然也是靜態(tài)存儲(chǔ)方式。這兩者在存儲(chǔ)方式上并無(wú)不同,這兩者的區(qū)別在于非靜態(tài)全局變量的作用域是整個(gè)源程序。當(dāng)一個(gè)源程序由多個(gè)源程序組成時(shí),非靜態(tài)的全局變量在各個(gè)源文件中都是有效的,而靜態(tài)全局變量則限制了其作用域,即只在定義該變量的源文件內(nèi)有效,在同一源程序的其他源文件中不能使用它。

這種在文件中進(jìn)行靜態(tài)聲明的做法是從C語(yǔ)言繼承而來(lái)的,在C語(yǔ)言中聲明為static的全局變量在其所在的文件外不可見(jiàn)。這種做法已經(jīng)被C++標(biāo)準(zhǔn)取消了,現(xiàn)在的替代做法是使用匿名命名空間。

匿名命名空間:指關(guān)鍵字namespace后緊跟花括號(hào)括起來(lái)的一系列聲明語(yǔ)句,具有如下特點(diǎn):

  • 在匿名命名空間內(nèi)定義的變量具有靜態(tài)生命周期
  • 匿名空間在某個(gè)給定的文件內(nèi)可以不連續(xù),但是不能跨越多個(gè)文件
  • 每個(gè)文件定義自己的匿名命名空間,不同文件匿名命名空間中定義的名字對(duì)應(yīng)不同實(shí)體
  • 如果在一個(gè)頭文件中定義了匿名命名空間,則該命名空間內(nèi)定義的名字在每個(gè)包含該頭文件的文件中對(duì)應(yīng)不同實(shí)體
namespace {
    int i;  // 匿名命名空間內(nèi)定義的變量具有靜態(tài)生命周期, 作用域僅限于當(dāng)前文件
}

3. 總結(jié)

static這個(gè)說(shuō)明符在不同地方所起的作用域是不同的,比如把局部變量改變?yōu)殪o態(tài)變量后是改變了它的存儲(chǔ)方式即改變了它的生存期,把全局變量改變?yōu)殪o態(tài)變量后是改變了它的作用域,限制了它的使用范圍。

auto

1. C++98中auto用法(C++11已廢棄)

C++98 auto用于聲明變量為自動(dòng)變量(擁有自動(dòng)的生命周期),C++11已經(jīng)刪除了該用法,取而代之的是“變量的自動(dòng)類(lèi)型推斷方法”。

// c++ 98:
int a = 10;         // 擁有自動(dòng)生命期
auto int b = 20;    // 擁有自動(dòng)生命期(C++11編譯不過(guò))
static int c = 30;  // 延長(zhǎng)了生命期

C++11新標(biāo)準(zhǔn)引入了auto類(lèi)型說(shuō)明符,讓編譯器通過(guò)初始值來(lái)自動(dòng)推斷變量類(lèi)型(這意味著通過(guò)auto定義的變量必須有初始值)。

// c++ 11:
int a = 10;
auto auto_a = a;  // 自動(dòng)類(lèi)型推斷為int類(lèi)型

2. auto會(huì)去除變量的引用語(yǔ)義

當(dāng)引用對(duì)象作為初始值時(shí),真正參與初始化的是引用對(duì)象的值,此時(shí)編譯器會(huì)以引用對(duì)象的類(lèi)型作為auto推算的類(lèi)型:

int main(void) {
    int i = 10;
    int &ri = i;
    auto auto_i = ri;  // 去除引用語(yǔ)義, 自動(dòng)推斷為int
}

如果希望推斷出來(lái)的auto類(lèi)型包含引用語(yǔ)義,我們需要用&明確指出:

int main(void) {
    int i = 10;
    auto &auto_i = i;  // 加上引用語(yǔ)義, 自動(dòng)推斷為int&
}

3. auto忽略頂層const

auto一般會(huì)忽略掉頂層const,同時(shí)底層const會(huì)被保留下來(lái):

int main(void) {
    const int ci = 10;    // 常量int
    auto auto_ci = ci;    // auto_ci被推斷為int類(lèi)型
    auto_ci = 20;         // 正確: auto_ci非常量
    const int &cr = ci;   // cr是指向常量int的常量引用
    auto auto_cr = cr;    // auto_cr被推斷為int類(lèi)型: 去除了引用語(yǔ)義 + 去除了頂層const
    auto_cr = 20;         // 正確: auto_cr非常量
    const int *cp = &ci;  // cp是指向常量int(底層)的常量指針(頂層)
    auto auto_cp = cp;    // auto_cp被推斷為const int*類(lèi)型(指向常量int的指針): 去除了頂層const + 保留底層const
    // *auto_cp = 10;     // 錯(cuò)誤: 不能修改auto_cp指向的常量
}

如果希望推斷出來(lái)的auto類(lèi)型是一個(gè)頂層const,我們需要通過(guò)const關(guān)鍵字明確指出:

int main(void) {
    const int ci = 10;          // 常量int
    const auto auto_ci = ci;    // auto_ci被推斷為const int類(lèi)型
    // auto_ci = 20;            // 錯(cuò)誤: auto_ci是一個(gè)常量, 禁止修改
}

const

有時(shí)我們希望定義一個(gè)不能被改變值的變量,可以使用關(guān)鍵字const對(duì)變量類(lèi)型加以限定。

1. const對(duì)象必須初始化

因?yàn)閏onst對(duì)象一經(jīng)創(chuàng)建后其值就不能再改變,所以const對(duì)象必須初始化,但是初始值可以是任意復(fù)雜的表達(dá)式:

const int i = get_size();  // 正確: 運(yùn)行時(shí)初始化
const int j = 42;          // 正確: 編譯時(shí)初始化
const int k;               // 錯(cuò)誤: k是一個(gè)未經(jīng)初始化的常量

2. 默認(rèn)情況下const僅在文件內(nèi)有效

舉個(gè)例子,我們?cè)诰幾g時(shí)初始化一個(gè)const對(duì)象:

const int i = 10;

編譯器會(huì)在編譯過(guò)程把用到該變量的地方都替換為對(duì)應(yīng)的值。為了執(zhí)行這個(gè)替換,編譯器必須知道變量的初始值,如果程序包含多個(gè)文件,那么每個(gè)用了這個(gè)const對(duì)象的文件都必須得能訪(fǎng)問(wèn)到它的初始值才行(即每個(gè)文件都要定義const對(duì)象)。為了避免對(duì)同一變量的重復(fù)定義,當(dāng)多個(gè)文件中出現(xiàn)同名的const對(duì)象時(shí),其實(shí)等同于在不同文件中分別定義了獨(dú)立的變量。

/*
 * 下面是合法的, 不存在變量i重復(fù)定義問(wèn)題
 */
// foo.cpp
const int i = 10;
// bar.cpp
const int i = 5;

如果想在多個(gè)文件之間共享const對(duì)象,那么必須在變量的定義之前添加extern關(guān)鍵字:

/*
 * 下面是合法的, main.cpp和foo.cpp中的const int對(duì)象是同一個(gè)
 */
// foo.cpp
extern const int i = 10;
// main.cpp
#include <iostream>
int main(void) {
    extern int i;
    std::cout << "i:" << i << std::endl;
}

3. 允許常量引用綁定非常量對(duì)象、字面值甚至一般表達(dá)式

一般而言,引用的類(lèi)型必須與其所引用對(duì)象的類(lèi)型一致,但是有兩個(gè)例外:

  • 初始化常量引用時(shí)允許用任意表達(dá)式作為初始值,只要該表達(dá)式的結(jié)果能轉(zhuǎn)換成引用類(lèi)型即可,允許為一個(gè)常量引用綁定非常量的對(duì)象、字面值甚至是一個(gè)一般表達(dá)式(如下)
  • 可以將基類(lèi)的指針或引用綁定到派生類(lèi)對(duì)象上(后續(xù)面向?qū)ο笳鹿?jié)再探討)
int i = 10;
const int &ri1 = i;      // 合法: 綁定到非常量對(duì)象
const int &ri2 = 100;    // 合法: 綁定到字面值
const int &ri3 = 1 + 1;  // 合法: 綁定到一般表達(dá)式

4. 頂層const與底層const

指針本身是一個(gè)對(duì)象,因此指針本身是不是常量與指針?biāo)笇?duì)象是不是常量是兩個(gè)獨(dú)立的問(wèn)題,前者被稱(chēng)為頂層const,后者被稱(chēng)為底層const。

Tips:指針類(lèi)型既可以是頂層const也可以是底層const,其他類(lèi)型要么是頂層常量要么是底層常量。

頂層const用于表示任意的對(duì)象是常量,包括算數(shù)類(lèi)型、類(lèi)和指針等,底層const用于表示引用和指針等復(fù)合類(lèi)型的基本類(lèi)型部分是否是常量。

int i = 10;
int *const p1 = &i;        // 頂層const: 不能改變p1的值
const int *p2 = &i;        // 底層const: 不能通過(guò)p2改變i的值
const int *const p3 = &i;  // 底層const + 頂層const
const int &r1 = i;         // 底層const: 不能通過(guò)r1改變i的值

constexpr

C++11引入了常量表達(dá)式constexpr的概念,指的是值不會(huì)改變并且在編譯期間就能得到計(jì)算結(jié)果的表達(dá)式。

const int i = 10;          // 常量表達(dá)式
const int j = i + 1;       // 常量表達(dá)式
const int k = size();      // 僅當(dāng)size()是一個(gè)constexpr函數(shù)時(shí)才是常量表達(dá)式, 運(yùn)行時(shí)才能獲得具體值就不是常量表達(dá)式

在一個(gè)復(fù)雜系統(tǒng)中,我們很難分辨一個(gè)初始值是否是常量表達(dá)式,通過(guò)constexpr關(guān)鍵字聲明一個(gè)變量,我們可以讓編譯器來(lái)驗(yàn)證變量的值是否是一個(gè)常量表達(dá)式。

1. 字面值是常量表達(dá)式

算術(shù)類(lèi)型、引用和指針都屬于字面值類(lèi)型,自定義類(lèi)則不屬于字面值類(lèi)型,因此也無(wú)法被定義為constexpr。

Tips:盡管指針和引用都能被定義成constexpr,但它們的初始值卻受到嚴(yán)格限制。一個(gè)constexpr指針的初始值必須是nullptr、0或者是存儲(chǔ)于某個(gè)固定地址中的對(duì)象。

2. constexpr是對(duì)指針的限制

在constexpr聲明中定義了一個(gè)指針,限定符constexpr僅對(duì)指針有效,與指針?biāo)笇?duì)象無(wú)關(guān):

const int *pi1 = nullptr;      // 底層const: pi1是指向整型常量的普通指針
constexpr int *pi2 = nullptr;  // 頂層const: pi2是指向整型的常量指針

我們也可以讓constexpr指針指向常量:

constexpr int i = 10;
constexpr const int *pi = &i;  // 頂層const + 底層const 

到此這篇關(guān)于[c++]變量聲明與定義的規(guī)則詳解的文章就介紹到這了,希望對(duì)大家有幫助,更多相關(guān)變量聲明與定義的規(guī)則內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論