靈活而奇特的C++語言特性——作用域解析(二)

       學習了博主的《漫談繼承技術》系列博文之後,相信大家都有所收穫吧!這次博主將和大家一起探討 《靈活而奇特的C++語言特性》 ,主要包括引用、常量(const)、常量表達式(constexpr)、靜態(static)、外部(expert)、類型定義(typedef)、類型別名(aliases)、類型轉換、作用域解析、統一初始化、顯示轉換運算符、特性(attribute)、用戶自定義文本、頭文件、可變長度參數列表和預處理器宏。儘管這個知識清單顯得有點凌亂,但是這些話題都是博主經過精心挑選,是容易混淆的語言特性。本篇我們來學習一下作用域解析的觀念以及使用場景,增進大家對《靈活而奇特的C++語言特性》的理解。

       C++程序猿必須熟悉作用域(scope)的概念。程序中的所有名稱,包括變量、函數和類名,都具有某種作用域。可以使用名稱空間、函數定義、花括號界定的塊和類定義創建作用域。當試圖訪問某個變量、函數或者類時,首先在最近作用域中查找這個名稱,然後是相鄰的作用域,依次類推,知道全局作用域。任何不在有名名稱空間、函數、花括號界定的塊和類中的名稱都被認爲在全局作用域中。如果在全局作用域中也找不到這個名稱,編譯器會給出一個未定義符號錯誤。那什麼是作用域運算符呢?作用域運算符就是“::”。下面就讓我們一起結合類的靜態成員來探討一下作用域運算符的使用。

類的靜態成員

       類的靜態成員可以實現對象與對象之間的數據共享。類屬性是描述類的所有對象的共同特徵的數據項,對於任何對象實例,它的屬性值是相同的。靜態數據成員具有靜態生存期,可以通過“類名::靜態數據成員名”來訪問。在類的定義中僅僅對靜態數據成員進行引用性聲明,必須在命名空間作用域的某個地方使用類名限定定義性聲明,這時也可以進行初始化。靜態函數成員既可以在類定義中實現,也可以先在類中聲明,再在類定義外實現,但static關鍵字只能寫在類定義中,否則編譯出錯。

提示:之所以類的靜態數據成員需要在類定義之外再加以定義,是因爲需要以這種方式專門爲它們分配空間。非靜態數據成員無須以此方式定義,因爲它們的空間是與它們所屬對象的空間同時分配的。

靜態成員函數可以通過類名或對象名來調用,而非靜態成員函數只能通過對象名來調用。

習慣:雖然靜態成員函數可以通過類名和對象名兩種方式調用,但一般習慣於通過類名調用。因爲即使通過對象名來調用,起作用的也只是對象的類型信息,與所使用的具體對象毫無關係。

       在靜態成員函數中訪問非靜態成員需要指明對象,因爲對靜態成員函數的調用是沒有目的對象的,因此不能像非靜態成員函數那樣,隱含的通過目的對象訪問類的非靜態成員。

 

        在靜態成員函數中只能訪問靜態成員,但是在非靜態成員函數中可以訪問靜態成員。靜態數據成員可以定爲const,但靜態成員函數不能定義爲const,否則編譯出錯。在對類的靜態數據成員(包括公有數據成員和私有數據成員)初始化的同時,還可以引用類的其他私有成員。咱們來舉個栗子吧。

#include <iostream>

using namespacestd;

 

class Point

{

public:

 

    //通過靜態成員函數訪問靜態數據成員m_pPtr,返回其屬性m_nValue

    staticintgetpPtr();

 

    //通過靜態成員函數訪問靜態數據成員m_P,返回其屬性m_nValue

    staticintgetP();

 

    //通過靜態成員函數釋放靜態數據成員m_pPtr在堆上的空間

    staticvoidDestroy();

 

    //析構函數

    virtual~Point();

 

private:

    //私有構造函數聲明

    Point(intnValue = 0);     

 

    intm_nValue;               //數據成員m_nValue

    //靜態數據成員m_pPtr,類的所有對象共享一份

    staticPoint *m_pPtr;  

    //靜態數據成員m_P,類的所有對象共享一份

    staticPoint m_P;      

};

 

//引用私有構造函數,對其進行初始化

Point *Point::m_pPtr= new Point(6);   

//引用私有構造函數,對其進行初始化

Point Point::m_P= Point(8);   

 

//私有構造函數實現

Point::Point(intnValue/*= 0*/)

    :m_nValue(nValue)

{

    cout<< "構造函數" << endl;

}

 

inline Point::~Point()

{

    cout<< "析構函數" << endl;

}

 

inline intPoint::getpPtr()

{

    //m_pPtr是指針,通過"->"來訪問

    returnm_pPtr->m_nValue;

}

 

inline intPoint::getP()

{

    //m_P是對象,通過"."來訪問

    returnm_P.m_nValue;

}

 

void Point::Destroy()

{

    if(nullptr!= m_pPtr)

    {

        //釋放靜態成員

        deletem_pPtr;

        //避免野指針

        m_pPtr= nullptr;

    }

}

 

int main(intargc,char**argv)

{

    //通過類名::(作用域運算符)訪問類的靜態方法

    cout<< Point::getpPtr() << endl;//6

    cout<< Point::getP() << endl;//8

 

    //顯式的釋放m_pPtr的空間

    Point::Destroy();

 

    //計算類對象的大小

    //32位系統上,sizeof(Point)值爲8,虛表指針和m_nValue各佔4個字節

    cout<< sizeof(Point)<< endl;

 

    return0;

}

 

程序運行結果:


       博主結合了作用域、可見性已經類的靜態成員和大家一起探討了作用域解析。作用域解析運算符(::)單獨使用的場景只有一處,就是在訪問全局命名空間中的名稱時候,其他都是結合着其他名稱來使用的,例如using 命名空間名稱::名稱、類名稱::靜態成員等。關於作用域解析我們就聊到這裏了,相信大家都有所收穫。

       如果想了解更多關於C++語言特性相關的知識,請關注博主《靈活而奇特的C++語言特性》系列博文,相信你能夠在那裏尋找到更多有助你快速成長和加深你對C++語言特性相關的知識和一些特性的理解和掌握。當然,如果你想了解關於繼承方面的技術,請關注博主《漫談繼承技術》系列博文。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章