C++入門(mén)系列博客四 const define static關(guān)鍵字

C++ 關(guān)鍵字 Const Define Static


作者:AceTan,轉(zhuǎn)載請(qǐng)標(biāo)明出處橘霎!


今天來(lái)討論一下在C++中很常見(jiàn)的三個(gè)關(guān)鍵字 Const Define Static.

0x00: const

const 限定符:有時(shí)候我們需要定義這樣一種變量仅孩,它的值是不可改變的塔粒。這時(shí)候上鞠,我們就需要用到const這個(gè)關(guān)鍵字了妓羊。

const 關(guān)鍵字在各大考試和C++筆試中經(jīng)常遇到驾讲。比如下面這一道面試題:

說(shuō)出const關(guān)鍵字在下列語(yǔ)句中的作用可婶。

  1. const int a;
  2. int const a;
  3. const int *a;
  4. int const *a;
  5. int *const a沿癞;
  6. const int * const a;
  7. const char* func1();
  8. int GetX() const;

看完是不是一臉懵逼?看完下面的解讀矛渴,上面的問(wèn)題將迎刃而解椎扬。

首先需要知道的是const修飾符所修飾的變量值不可改變惫搏。

另外需要注意的是,在用const定義關(guān)鍵字時(shí)蚕涤, const int 和 int const 這兩種寫(xiě)法是等價(jià)的(沒(méi)有*)筐赔,它表示所定義的整型變量的值是不可改變的。

現(xiàn)在我們的關(guān)鍵問(wèn)題是揖铜,const修飾符到底是修飾的誰(shuí)茴丰。是這個(gè)整型變量,還是指針天吓,還是指針?biāo)赶虻闹怠?/p>

默認(rèn)狀態(tài)下,const 對(duì)象僅在文件內(nèi)有效贿肩。 如果想讓它在其他文件內(nèi)也有效,需要添加extern關(guān)鍵字龄寞。

引用和const

const 的引用 可以把引用綁定到const對(duì)象上汰规,就像綁定到其他對(duì)象上一樣,我們稱之為對(duì)常量的引用(reference to const)萄焦。 與普通引用不同的是控轿,對(duì)常量的引用不能被用作修改它所綁定的對(duì)象。

舉個(gè)栗子:

int v = 123;
const int& r1 = v;        // 允許將const int& 綁定到一個(gè)普通的int對(duì)象上
const int& r2 = 123;    // 正確:r2是一個(gè) 常量引用
const int& r3 = r1 * 10;// 正確:r3是一個(gè)常量引用
int& r4 = r1 * 5;        // 錯(cuò)誤:r4是一個(gè)普通的非常量引用

int& r5 = v;            // r5綁定對(duì)象v
r5 = 1;                    // 正確,r5并非一個(gè)常量
r1 = 1;                    // 錯(cuò)誤:r1是一個(gè)常量引用拂封,值不可更改

在實(shí)際編程中茬射,如果寫(xiě)的語(yǔ)法有錯(cuò)誤,IDE一般都是會(huì)提示的冒签,根據(jù)提示修改就好在抛。 但我們應(yīng)該記住這些常見(jiàn)的特性。

指針和const

指針和const鬼混在一塊萧恕,那才是噩夢(mèng)的開(kāi)始刚梭。

指針是對(duì)象,而引用不是票唆。因此朴读,可以像其他對(duì)象類型一樣,允許指針本身定為常量走趋。常量指針(const pointer)必須要初始化衅金。

int a = 0;
int* const p = &a;                // p是一個(gè)常量指針,指向整型數(shù)a
const double pi = 3.14159;
const double* const pPi = π    // pPi是一個(gè)指向常量對(duì)象pi的常量指針

是不是看著有點(diǎn)頭暈簿煌,理清這些關(guān)系的最好方法是從右往左讀氮唯。

拿上面的第四個(gè)來(lái)解釋一下。離pPi最近的是const姨伟,說(shuō)明pPi是一個(gè)常量對(duì)象惩琉。對(duì)象的類型是什么呢,繼續(xù)往左讀夺荒,對(duì)象的類型由聲明符的其余部分所決定的瞒渠,聲明符的下一個(gè)是*, 意思是pPi是一個(gè)常量指針良蒸。該聲明語(yǔ)句的基本數(shù)據(jù)類型(double)確定了常量對(duì)象(pPi)指向了一個(gè)double型對(duì)象。再往左讀還是個(gè)const伍玖,說(shuō)明這個(gè)double型對(duì)象也是個(gè)常量诚啃。

C++ 中有兩個(gè)術(shù)語(yǔ)對(duì)指針是否是常量,以及指針指向的對(duì)象是否是常量加以區(qū)分私沮。

頂層const(top-level const) 表示指針本身是個(gè)常量始赎。

底層const(low-level const) 表示指針?biāo)傅膶?duì)象是一個(gè)常量。

這兩個(gè)問(wèn)題是獨(dú)立的仔燕。

const的常見(jiàn)用法

  • const可以用來(lái)定義常量造垛。但它更大的魅力是它可以修飾函數(shù)的參數(shù)、返回值晰搀,甚至函數(shù)的定義體五辽。這也是實(shí)際應(yīng)用中用的比較多的。

use const whenever you need

  • const修飾函數(shù)參數(shù)外恕。const修飾參數(shù)杆逗,是防止傳入的參數(shù)被意外的修改。前面講到過(guò)鳞疲,可以用引用或者指針作為參數(shù)來(lái)作為輸出罪郊。 這樣是不需要加const的。其他一些情況尚洽,如果傳入的參數(shù)不需要被改變悔橄,則需要加const修飾∠俸粒基本類型一般是不需要const進(jìn)行修飾的癣疟,因?yàn)樗扇≈祵_f的方式。如果是用戶自定義類型潮酒,我們一般采用引用傳遞睛挚,像 Function(A& a),其中A是用戶自定義的類型。 Function(A a)的效率不如Function(A& a),因?yàn)楹瘮?shù)體內(nèi)將產(chǎn)生A類型的臨時(shí)對(duì)象用于復(fù)制參數(shù)a急黎,而臨時(shí)對(duì)象的構(gòu)造扎狱、復(fù)制、析構(gòu)過(guò)程都將消耗時(shí)間叁熔。但這樣聲明有一個(gè)缺點(diǎn)委乌,它會(huì)使調(diào)用者誤認(rèn)為出入的引用時(shí)可修改的床牧,解決的方法就是加const進(jìn)行修飾荣回。

如果傳入的參數(shù)不需要更改,那么加const修飾在實(shí)際編程中幾乎是必須的戈咳。

  • const修飾函數(shù)的返回值心软。const一般用來(lái)修飾以"指針傳遞"方式的函數(shù)返回值壕吹,它表示函數(shù)的返回值(即指針)的內(nèi)容是不可修改的。該返回值只能被賦給加const修飾的同類型指針删铃。修飾以"值傳遞"方式的函數(shù)返回值是沒(méi)有意義的耳贬。

const  char* GetStr();
const char* someStr = GetStr();        // 正確
char* str2 = GetStr();                // 錯(cuò)誤
  • const修飾成員函數(shù)。 任何不會(huì)修改數(shù)據(jù)成員的函數(shù)都應(yīng)該聲明為const類型猎唁。確切的說(shuō)咒劲,const是修飾this指向的對(duì)象的。

class A 
{
private:
    int num;                // 成員變量

public:
    void Func(int x);        // 其實(shí)原型是兩個(gè)參數(shù) Func(A* const this, int x)

    void Func2(int y) const;// 原型是Func2(const A* const this, int y)

};

void A::Func(int x)
{
    num = num * x;        // 正確:this所指向的對(duì)象沒(méi)有const修飾诫隅,可以更改
}

void A::Func2(int y) const
{
    num = num * y;       // 錯(cuò)誤:this所指向的對(duì)象被const修飾腐魂,無(wú)法更改這個(gè)對(duì)象的數(shù)據(jù)。
}

至于const為什么放在后面逐纬,想必各位看官已經(jīng)猜到了蛔屹。原型的第一個(gè)參數(shù)是被省略了,無(wú)法修飾豁生,大概也只能放到函數(shù)的后面了兔毒,雖然看起來(lái)怪怪的。


0x01: define

準(zhǔn)確的說(shuō)應(yīng)該是#define甸箱。 它繼承自C語(yǔ)言育叁,是一個(gè)預(yù)處理指令。所謂的預(yù)處理芍殖,就是在編譯之前執(zhí)行一段程序擂红,可以部分的改變我們寫(xiě)的程序。 之前我們見(jiàn)過(guò)的#include就是一條預(yù)處理命令围小,它將所包含的文件替換到所用指令的地方昵骤。#define指令是用來(lái)把一個(gè)名字定義預(yù)處理變量的。預(yù)處理變量有兩種狀態(tài):已定義與未定義肯适。#define是用來(lái)定義它的变秦。與之對(duì)應(yīng)的是另外兩個(gè)指令分別檢查預(yù)處理變量是否已經(jīng)定義。#ifdef 當(dāng)且僅當(dāng)變量已定義時(shí)為真框舔,#ifndef當(dāng)且僅當(dāng)變量未定義時(shí)為真蹦玫。一旦檢查為真,則執(zhí)行后續(xù)的操作直到遇到#endif 指令為止刘绣。

預(yù)處理變量無(wú)視C++語(yǔ)言中關(guān)于作用域的規(guī)則

#define 的常見(jiàn)用法

  • 頭文件保護(hù)(header guard)樱溉。在定義一個(gè)頭文件時(shí),建議習(xí)慣性地加上頭文件保護(hù)纬凤,沒(méi)必要太在乎你的程序到底需不需要福贞。這是一個(gè)比較好的編程習(xí)慣。 一般預(yù)處理變量名我們用所要定義的類名的大寫(xiě)來(lái)命名停士。示例如下:

#ifndef _FORM_RIDE_H_
#define _FORM_RIDE_H_

// 你的代碼

#endif // _FORM_RIDE_H_

加了頭文件保護(hù)挖帘,別人就不用擔(dān)心是否重復(fù)引入你的文件了完丽。

  • 改變程序執(zhí)行流程。 平臺(tái)的差異性拇舀,客服端和服務(wù)器的差異性逻族,有時(shí)候?yàn)榱俗尨a具有更好的兼容性,通常定義這些宏來(lái)執(zhí)行不同的邏輯代碼骄崩。這時(shí)候只需要一個(gè)總的控制文件聘鳞,就能讓代碼在不同的情形下執(zhí)行不同的邏輯。

#ifdef _CLIENT_
    if (pEntity == NULL)
    {
        return false;
    }
    string pathname = string(pEntity->GetCore()->GetResourcePath()) + "share/rule/ridestrength/ride_strength_info.xml";
#elif defined(_SERVER_STUB_)
    string pathname = string(pKernel->GetResourcePath()) + "share/rule/ridestrength/ride_strength_info.xml";
#elif defined(_SERVER_MEMBER_) || defined(_SERVER_ROOM_)
    string pathname = string(pKernel->GetResourcePath()) + "ini/rule/ridestrength/ride_strength_info.xml";
#endif
  • 充當(dāng)函數(shù)的作用要拂。說(shuō)是充當(dāng)函數(shù)搁痛,其實(shí)和函數(shù)還是有挺大差別。只是在用到的地方進(jìn)行替換宇弛,使程序看起來(lái)更簡(jiǎn)潔鸡典。下面的代碼是用來(lái)檢查循環(huán)的,可以避免死循環(huán)枪芒。預(yù)設(shè)一個(gè)循環(huán)最大值彻况,然后調(diào)用這個(gè)宏讓它檢查。

#include <iostream>

using namespace std;

int g_nMaxCirculateCount = 100;

void SetMaxCirculateCount(int count)
{
    if (g_nMaxCirculateCount != count)
    {
        g_nMaxCirculateCount = count;
    }
}

#define  LoopBeginCheck(name) \
     int nCheck##name##Count = 0;

#define  LoopDoCheck(name) \
        nCheck##name##Count++;\
        if((g_nMaxCirculateCount > 0) && (nCheck##name##Count > g_nMaxCirculateCount))\
        {\
            cout << "文件路徑:" << __FILE__ << endl \
                 << "函數(shù):" << __FUNCTION__<< endl \
                 << "所在行:" << __LINE__ << endl \
                 << "循環(huán)次數(shù):" << nCheck##name##Count << endl; \
            break;\
        }


int main()
{
    int n = 77 * 88;            // 實(shí)際需要執(zhí)行的循環(huán)次數(shù)

    SetMaxCirculateCount(10);    // 設(shè)置的最大循環(huán)次數(shù)
    LoopBeginCheck(var);
    for (int i = 1; i < n; i++)
    {
        LoopDoCheck(var);

        cout << "循環(huán)結(jié)束" << endl;
    }
 
    return 0;
}

這里的 \ 起鏈接換行的作用舅踪,##name## 是拼接參數(shù)纽甘。 __FILE__等則是一些內(nèi)置宏。

0x02: static

static定義的變量存儲(chǔ)在靜態(tài)數(shù)據(jù)區(qū),在靜態(tài)數(shù)據(jù)區(qū)抽碌,內(nèi)存中所有的字節(jié)默認(rèn)值都是0x00悍赢。

靜態(tài)變量作用范圍在一個(gè)文件內(nèi),程序開(kāi)始時(shí)分配空間货徙,結(jié)束時(shí)釋放空間左权,默認(rèn)初始化為0,使用時(shí)可以改變其值痴颊。與全局變量不同的是赏迟,靜態(tài)變量或靜態(tài)函數(shù)只有本文件內(nèi)的代碼才能訪問(wèn)它,它的名字在其它文件中不可見(jiàn)蠢棱。

static的主要有以下三個(gè)特性:

  • 表示代碼退出一個(gè)塊后锌杀,仍然能夠存在的局部變量。因?yàn)閿?shù)據(jù)會(huì)存儲(chǔ)在靜態(tài)數(shù)據(jù)區(qū)泻仙。

  • 用來(lái)表示不能被其它文件訪問(wèn)的全局變量和函數(shù)糕再。

  • 表示屬于一個(gè)類而不是屬于此類的任何特定對(duì)象的變量和函數(shù)。這個(gè)和Java中static關(guān)鍵字的意義相同玉转。

有了static關(guān)鍵字突想,變量就變的有些復(fù)雜了。是時(shí)候理清一下各類型變量的作用域范圍了。

常見(jiàn)的變量分為如下幾種:全局變量蒿柳、靜態(tài)全局變量、靜態(tài)局部變量和局部變量漩蟆。

  • 全局變量垒探。存儲(chǔ):靜態(tài)存儲(chǔ)區(qū)域。 作用域:在整個(gè)工程文件內(nèi)都有效怠李。

  • 靜態(tài)全局變量圾叼。存儲(chǔ):靜態(tài)存儲(chǔ)區(qū)域。 作用域:在定義它的文件內(nèi)有效捺癞。

  • 靜態(tài)局部變量 存儲(chǔ):靜態(tài)存儲(chǔ)區(qū)域夷蚊。 作用域:只在定義它的函數(shù)內(nèi)有效。程序僅分配一次內(nèi)存,函數(shù)返回后髓介,改變量不會(huì)消失惕鼓。

  • 局部變量 存儲(chǔ):內(nèi)存棧中。 作用域:只在定義它的函數(shù)內(nèi)有效唐础。程序返回后局部變量被回收箱歧。

static函數(shù)在內(nèi)存中只有一份,普通函數(shù)在每個(gè)被調(diào)用中維持一份拷貝一膨。


#include <iostream>

using namespace std;

int n = 100;            // 全局變量
static int m;            // 靜態(tài)全局變量

class MyClass
{
public:

    void ChangeX(int x);        // 普通成員函數(shù)
    static void ChangeY(int y);    // 靜態(tài)成員函數(shù)

    int GetY();                    // 獲取全局變量的值

private:
    int m_x;                    // 普通成員變量
    static int m_y;                // 靜態(tài)成員變量 

};


void MyClass::ChangeX(int x)
{
    ++m_x;

    static int times = 0;
    ++times;
    cout << "第" << times << "次調(diào)用該函數(shù)" << endl;
}

void MyClass::ChangeY(int y)
{
    //m_x = y;        // 報(bào)錯(cuò):靜態(tài)成員函數(shù)只能引用靜態(tài)成員變量
    m_y = y;
}

int MyClass::GetY()
{
    cout << "靜態(tài)成員變量y的值為:" << m_y << endl;
    return m_y;
}


int MyClass::m_y = 0;//定義并初始化靜態(tài)數(shù)據(jù)成員 

int main()
{
    n = 88;
    cout << "全局變量改為" << n << endl;

    cout << "靜態(tài)全局變量的初始值為" << m << endl;

    MyClass cls1;            // 創(chuàng)建第一個(gè)類
    cls1.ChangeY(111);        // 改變了靜態(tài)成員的值

    MyClass cls2;            // 創(chuàng)建第一個(gè)類
    cls2.ChangeX(1);        // 連續(xù)調(diào)用3次
    cls2.ChangeX(2);
    cls2.ChangeX(3);

    cls2.GetY();            // 直接輸出靜態(tài)成員變量看看

    return 0;

}

輸出結(jié)果:
全局變量改為88

靜態(tài)全局變量的初始值為0

第1次調(diào)用該函數(shù)

第2次調(diào)用該函數(shù)

第3次調(diào)用該函數(shù)

靜態(tài)成員變量y的值為:111

輸出結(jié)果印證了上面所討論的內(nèi)容呀邢。

0x03: 結(jié)束語(yǔ)

const define static在實(shí)際編程中應(yīng)用的非常多,各位看官應(yīng)多加理解豹绪,靈活應(yīng)用价淌。

C++
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市瞒津,隨后出現(xiàn)的幾起案子蝉衣,更是在濱河造成了極大的恐慌,老刑警劉巖巷蚪,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件买乃,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡钓辆,警方通過(guò)查閱死者的電腦和手機(jī)剪验,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)前联,“玉大人功戚,你說(shuō)我怎么就攤上這事∷凄停” “怎么了啸臀?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我乘粒,道長(zhǎng)豌注,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任灯萍,我火速辦了婚禮轧铁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘旦棉。我一直安慰自己齿风,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布绑洛。 她就那樣靜靜地躺著救斑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪真屯。 梳的紋絲不亂的頭發(fā)上脸候,一...
    開(kāi)封第一講書(shū)人閱讀 51,727評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音绑蔫,去河邊找鬼纪他。 笑死,一個(gè)胖子當(dāng)著我的面吹牛晾匠,可吹牛的內(nèi)容都是我干的茶袒。 我是一名探鬼主播,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼凉馆,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼薪寓!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起澜共,我...
    開(kāi)封第一講書(shū)人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤向叉,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后嗦董,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體母谎,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年京革,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了奇唤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡匹摇,死狀恐怖咬扇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情廊勃,我是刑警寧澤懈贺,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響梭灿,放射性物質(zhì)發(fā)生泄漏画侣。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一堡妒、第九天 我趴在偏房一處隱蔽的房頂上張望配乱。 院中可真熱鬧,春花似錦涕蚤、人聲如沸宪卿。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至西疤,卻和暖如春烦粒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背代赁。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工扰她, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人芭碍。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓徒役,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親窖壕。 傳聞我的和親對(duì)象是個(gè)殘疾皇子忧勿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容