天天看點

C++(存儲持續性,作用域(包含類)及連結性,名稱空間)

C++有三種管理資料記憶體的方式:自動存儲,靜态存儲和動态存儲(C++11中還有一種,就是線程存儲)

C++(存儲持續性,作用域(包含類)及連結性,名稱空間)
  • 自動存儲

在函數内部定義的正常變量使用自動存儲空間,被稱為自動變量(局部變量,作用域隻是限于包含它的代碼塊)

這就意味着它們在所屬的函數被調用時自動産生,在該函數結束時消亡。

  • 靜态存儲

靜态存儲是整個程式執行期間都存在的存儲方式。使變量成為靜态的方式有兩種:

  1. 是在函數外面定義它(全局變量)
  2. 聲明時使用static
  • 動态存儲

new和delete提供了一種比自動變量和靜态變量更靈活的方法,他們管理一個記憶體池,自由存儲空間(free store)或棧(heap)

該記憶體池同樣用于靜态變量和自動變量的記憶體是分開的。可以在一個函數中new,在另一個函數中delete, 進行釋放。

  • 存儲持續性,作用域及連結性

在C++11中,使用四種方案來存儲資料

  • 自動存儲持續性

在函數定義中聲明的變量的存儲持續性為自動的。

  • 靜态存儲持續性

在函數外定義的變量和使用關鍵字static定義的變量。

  • 線程存儲持續性

再論

  • 動态存儲持續性

用new運算符配置設定的記憶體将一直存在,直到使用delete運算符将其釋放或程式結束為止。

存儲持續性描述變量是如何存儲的。

作用域(scope)描述了名稱在檔案的多大範圍可見。

連結性(linkage)描述了名稱如何在不同單元間共享。

下面具體介紹存儲方式的特征。

自動存儲持續性:

在函數中聲明的函數參數和變量,存儲持續性為自動,作用域為局部,沒有連結性。

以上概念很好了解,當出現特例時,比如使用兩個同名變量,一個處于外部代碼塊中,一個處于内部代碼塊中

情況如下:

C++(存儲持續性,作用域(包含類)及連結性,名稱空間)
  • 自動變量和棧

因為自動變量的數目随函數的開始和結束而增減,是以程式必須在運作時對自動變量進行管理

常用的方法是留出一段記憶體,将其視為棧。之是以稱為棧,是因為新資料被象征性地放在原有資料的上面,當程式使用

兩個指針來跟蹤棧,一個指針指向棧底——棧開始的地方,另一個指向棧頂——下一個記憶體單元。當函數被調用時,

其自動變量将被加入到棧中,棧頂指針指向變量後的下一個可用的記憶體單元。

函數結束時,棧頂指針被重置為函數被調用前的值,進而釋放新變量使用的記憶體。

棧是LIFO先進後出,後進先出,即最後加入到棧中的變量首先被彈出。

C++(存儲持續性,作用域(包含類)及連結性,名稱空間)

靜态持續變量

C++的靜态存儲持續性提供了三種連結性:

  1. 外部連結性(必須在代碼塊的外面聲明它)【可在其他檔案中通路】
  2. 内部連結性(必須在代碼塊的外面聲明它,并使用static限定符)【隻能在目前檔案中通路】
  3. 無連結性(必須在代碼塊内部聲明它,并使用static限定符)【隻能在目前函數或代碼塊中通路】

注意:靜态變量的數目在程式運作期間是不變的,是以,程式不需要使用特殊的裝置(如棧)來管理他們。

且靜态變量預設初始化為零。

C++(存儲持續性,作用域(包含類)及連結性,名稱空間)
...
int global = 1000;//stctic duration,external linkage
static int one_file = 50;//static duration,internal linkage
int main()
{
...
}

void funct1(int n)
{
    static int count = 0;//stctic duration,no linkage
    int llama = 0;
}
           

總結:

C++(存儲持續性,作用域(包含類)及連結性,名稱空間)

 靜态持續性,外部連結性

連結性為外部的變量通常稱為外部變量,他們的存儲持續性為靜态,作用域為整個檔案。

相對于局部的自動變量,外部變量也可以稱為全局變量。

  • 單定義規則與引用聲明

C++提出了兩種變量聲明的方式:

  1. 定義聲明簡稱定義
  2. 引用聲明簡稱聲明

關于單定義規則:變量隻能有一次定義。

extern一般是使用在多檔案之間需要共享某些代碼時

關于聲明:使用關鍵字extern,它不給變量配置設定空間,因為它引用已有的變量。

如果多個檔案需要使用同一個變量,隻需要在某一個檔案中進行定義即可,在其他檔案中進行引用聲明即可。

//file01.cpp
extern int v = 2;//如果指派,就是定義,不指派,就是引用
int v = 2;     //這兩個語句效果完全一樣,都是v的定義

int pr = 2;
int ceu;

//file02.cpp
extern int v;
extern int pr;

//file03.cpp
extern int v;
extern int pr;
extern int ceu;
           

 靜态持續性,内部連結性

将static限定符用于作用域為整個檔案的變量時,該變量的連結性将為内部的。

可以将static了解為錨,固定作用域。

靜态持續性,無連結性

将static限定符用于作用域為代碼塊内的變量時,該變量的連結性将為内部的。

可以将static了解為錨,固定作用域

twofile1.cpp
/
#include<iostream>
int tom = 3;//靜态存儲持續性,外部連結性
int dick = 30;//靜态存儲持續性,外部連結性
static int harry = 300;//靜态存儲持續性,内部連結性
void remote_access();
int main()
{ 
   using namespace std;
   cout<<&tom<<" = &tom "<<&dick<<" = &dick";
   cout<<&harry<<" = harry";
   remote_access();
   return 0;
}

twofile2.cpp

#include<iostream>
extern int tom;//引用聲明
static int dick = 30; //靜态存儲持續性,内部連結性
int harry = 300;//靜态存儲持續性,外部連結性

void remote_access()
{
   using namespace std;
   cout<<&tom<<" = &tom "<<&dick<<" = &dick";
   cout<<&harry<<" = harry";
}

結果
020 = &tom 024 = &dick 028 = &harry
020 = &tom 450 = &dick 454 = &harry
           

兩個檔案使用了同一個tom變量

所有函數的存儲持續性都自動為靜态。

Tips:

關于全局變量和局部變量

結論:盡量使用局部變量,全局變量固然好,随時都可以通路,但是易于通路的代價很大,程式不可靠,而且降低程式運作的速度。程式越能避免對資料進行不必要的通路,就越能保持資料的完整性。

類作用域:

在類中定義的名稱(如類資料成員名和類成員函數名)的作用域都為整個類,作用域為整個類的名稱隻在該類中是已知的

在類外是不可知的。

是以,可以在不同類中使用相同的類成員名而不會引起沖突。

特别的,指針通路類成員,使用間接成員運算符(->),普通變量,使用成員運算符(.)
Ik * pr = new Ik;
Ik ee = Lk(8);//構造函數初始化
ee.show();
pr->show();
           

名稱空間namespace(避免名稱沖突)

在C++中,名稱可以是變量,函數,結構,枚舉,類及結構的成員。

當随着項目的增大,名稱互相沖突的可能性也将增加。

使用多個廠商的類時,難免會出現名稱沖突。

C++提供了名稱空間工具,以便更好地控制名稱的作用域。

明确概念:

  1. 聲明區域(declaration region):聲明區域是可以在其中進行聲明的區域。
  2. 潛在作用域(potential scope):變量的潛在作用域從聲明點開始,到其聲明區域的結尾。
  3. 名稱空間(namespace):通過定義一種新的聲明區域來建立命名的名稱空間,提供一個聲明名稱的區域。
C++(存儲持續性,作用域(包含類)及連結性,名稱空間)
namespcae Jack{
    double pail;
    void fetch();
    int pal;
    struct well{...};
}

namespace Jill
{
    double bucket(double n);
    double fetch;
    int pal;
    struct Hill{...};
}
           

名稱空間是全局的

當然也可以位于另一個名稱空間中,但不能位于代碼塊中。

是以名稱空間中聲明的名稱的連結性為外部的。

任何名稱空間中的名稱都不會和其他名稱空間中的名稱發生沖突。

往名稱空間中添加元素:

namespace Jill{
     char * goose(const char *);
}
           

也可以為fetch()函數提供原型:(可以在檔案的後面甚至另一個檔案中,連結性為外部)

namespcae Jack
{
    void fetch()
    {
      ....
    }

}
           

當我們需要對名稱空間中的變量進行通路,需要做什麼工作呢?

使用作用域解析運算符::

Jack::pail = 12.34;
Jill::Hill mole;
Jack::fetch();
           
//Qt中的名稱空間
namespace Ui {
class MainWindow;
}//名稱空間的名稱為Ui,内部包含類MainWindow

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
    double pr[4][5];

private:
    Ui::MainWindow *ui;
   //因為沒有使用using聲明或者using編譯指令
   //直接使用Ui及作用域運算符
};
           
  • using聲明和using編譯指令

C++使用using聲明和using編譯指令兩種方法來簡化對名稱空間中的名稱的使用。

  • 首先介紹using聲明(簡化了表達方式)

将特定的名稱添加到它所屬的聲明區域中,可以是全局聲明區域,也可以是局部聲明區域。

此時,再通路變量時,就可以省略Jill::了

進一步聲明兩個變量:

  1. 全局名稱空間(global namespace)
  2. 局部名稱空間(local namespace)

局部聲明區域示範:

namespace Jill
{
   double bucket (double n){...};
   double fetch;
   struct Hill{...};
}
char fetch;//global namespace
int main()
{
    using Jill::fetch;//局部聲明區域,進入局部名稱空間
    double fetch;//錯誤,因為已經有上一句了,局部的聲明區域已經定義了double fetch;
    cin>>fetch;//read a value into Jill::fetch
    cin>>::fetch;//調用的是global 中的fetch
    //局部變量會覆寫同名的全局變量
}
           

全局變量示範:

namespace Jill
{
   double bucket (double n){...};
   double fetch;
   struct Hill{...};
}
using Jill::fetch//全局聲明區域,進入全局聲明空間
void other();
int main()
{
   cin>>fetch;//read value into Jill::fetch
   other();
}
void otther()
{
   cout <<fetch;//display Jill::fetch
}
           
  • using編譯指令

using聲明隻是對namespace中的一個名稱有用,而using編譯指令對所有的名稱都可用。

它使名稱空間中的所有名稱都可用

#include<iostream>
using namespace std;//名稱空間中的所有名稱均可直接調用,不需要作用域解析運算符::
           
  • using聲明和using編譯指令的差別
//using聲明
namespace Jill
{
   double bucket (double n){...};
   double fetch;
   struct Hill{...};
}
char fetch;//global namespace
int main()
{
    using Jill::fetch;//局部聲明區域,進入局部名稱空間
    double fetch;//錯誤,因為已經有上一句了,局部的聲明區域已經定義了double fetch;
    cin>>fetch;//read a value into Jill::fetch
    cin>>::fetch;//調用的是global 中的fetch
    //局部變量會覆寫同名的全局變量
}
//using編譯指令
namespace Jill
{
   double bucket (double n){...};
   double fetch;
   struct Hill{...};
}
char fetch;//global namespace
int main()
{
    using namespace Jill;//局部聲明區域,進入局部名稱空間
    double water = bucker(2);//可用直接調用而不需要使用“::”
    Hill Thrill;

    double fetch;//這個沒錯,如果是在using中就是錯的,而且還會覆寫掉Jill::fetch
    cin>>fetch;//調用的是上面的fetch
    cin>>::fetch;//調用的是global 中的fetch
    cin>>Jill::fetch;//read a value into Jill::fetch

}
           

總上:

  • using聲明,在作用域内,絕對不允許有和using聲明的名稱同名的變量。
  • using編譯指令下,允許,而且會覆寫原來同名稱的變量。
  • 同理:通路global變量時,都需要使用"::fetch"。