二、爲何要引入static?
函數內部定義的變量,在程序執行到它的定義處時,編譯器爲它在棧上分配空間,你們知道,函數在棧上分配的空間在此函數執行結束時會釋放掉,這樣就產生了一個問題: 若是想將函數中此變量的值保存至下一次調用時,如何實現? 最容易想到的方法是定義一個全局的變量,但定義爲一個全局變量有許多缺點,最明顯的缺點是破壞了此變量的訪問範圍(使得在此函數中定義的變量,不只僅受此函數控制)。ios
三、何時用static?
須要一個數據對象爲整個類而非某個對象服務,同時又力求不破壞類的封裝性,即要求此成員隱藏在類的內部,對外不可見。程序員
四、static的內部機制:
靜態數據成員要在程序一開始運行時就必須存在。由於函數在程序運行中被調用,因此靜態數據成員不能在任何函數內分配空間和初始化。
這樣,它的空間分配有三個可能的地方,一是做爲類的外部接口的頭文件,那裏有類聲明;二是類定義的內部實現,那裏有類的成員函數定義;三是應用程序的main()函數前的全局數據聲明和定義處。
靜態數據成員要實際地分配空間,故不能在類的聲明中定義(只能聲明數據成員)。類聲明只聲明一個類的「尺寸和規格」,並不進行實際的內存分配,因此在類聲明中寫成定義是錯誤的。它也不能在頭文件中類聲明的外部定義,由於那會形成在多個使用該類的源文件中,對其重複定義。
static被引入以告知編譯器,將變量存儲在程序的靜態存儲區而非棧上空間,靜態
數據成員按定義出現的前後順序依次初始化,注意靜態成員嵌套時,要保證所嵌套的成員已經初始化了。消除時的順序是初始化的反順序。安全
五、static的優點:
能夠節省內存,由於它是全部對象所公有的,所以,對多個對象來講,靜態數據成員只存儲一處,供全部對象共用。靜態數據成員的值對每一個對象都是同樣,但它的值是能夠更新的。只要對靜態數據成員的值更新一次,保證全部對象存取更新後的相同的值,這樣能夠提升時間效率。多線程
六、引用靜態數據成員時,採用以下格式:
<類名>::<靜態成員名>
若是靜態數據成員的訪問權限容許的話(即public的成員),可在程序中,按上述格式來引用靜態數據成員。函數
七、注意事項:
(1)類的靜態成員函數是屬於整個類而非類的對象,因此它沒有this指針,這就致使了它僅能訪問類的靜態數據和靜態成員函數。
(2)不能將靜態成員函數定義爲虛函數。
(3)因爲靜態成員聲明於類中,操做於其外,因此對其取地址操做,就多少有些特殊,變量地址是指向其數據類型的指針 ,函數地址類型是一個「nonmember函數指針」。this
(4)因爲靜態成員函數沒有this指針,因此就差很少等同於nonmember函數,結果就產生了一個意想不到的好處:成爲一個callback函數,使得咱們得以將C++和C-based X Window系統結合,同時也成功的應用於線程函數身上。
(5)static並無增長程序的時空開銷,相反她還縮短了子類對父類靜態成員的訪問時間,節省了子類的內存空間。
(6)靜態數據成員在<定義或說明>時前面加關鍵字static。
(7)靜態數據成員是靜態存儲的,因此必須對它進行初始化。
(8)靜態成員初始化與通常數據成員初始化不一樣:
初始化在類體外進行,而前面不加static,以避免與通常靜態變量或對象相混淆;
初始化時不加該成員的訪問權限控制符private,public等;
初始化時使用做用域運算符來標明它所屬類;
因此咱們得出靜態數據成員初始化的格式:
<數據類型><類名>::<靜態數據成員名>=<值>
(9)爲了防止父類的影響,能夠在子類定義一個與父類相同的靜態變量,以屏蔽父類的影響。這裏有一點須要注意:咱們說靜態成員爲父類和子類共享,但咱們有重複定義了靜態成員,這會不會引發錯誤呢?不會,咱們的編譯器採用了一種絕妙的手法:name-mangling 用以生成惟一的標誌。 spa
靜態數據成員
在類中,靜態成員能夠實現多個對象之間的數據共享,而且使用靜態數據成員還不會破壞隱藏的原則,即保證了安全性。所以,靜態成員是類的全部對象中共享的成員,而不是某個對象的成員。
使用靜態數據成員能夠節省內存,由於它是全部對象所公有的,所以,對多個對象來講,靜態數據成員只存儲一處,供全部對象共用。靜態數據成員的值對每一個對象都是同樣,但它的值是能夠更新的。只要對靜態數據成員的值更新一次,保證全部對象存取更新後的相同的值,這樣能夠提升時間效率。
靜態數據成員的使用方法和注意事項以下:
一、靜態數據成員在定義或說明時前面加關鍵字static。
二、靜態成員初始化與通常數據成員初始化不一樣。靜態數據成員初始化的格式以下:
<數據類型><類名>::<靜態數據成員名>=<值>
這代表:線程
(1) 初始化在類體外進行,而前面不加static,以避免與通常靜態變量或對象相混淆。
(2) 初始化時不加該成員的訪問權限控制符private,public等。
(3) 初始化時使用做用域運算符來標明它所屬類,所以,靜態數據成員是類的成員,而不是對象的成員。
三、靜態數據成員是靜態存儲的,它是靜態生存期,必須對它進行初始化。
四、引用靜態數據成員時,採用以下格式:
<類名>::<靜態成員名>
若是靜態數據成員的訪問權限容許的話(即public的成員),可在程序中,按上述格式來引用靜態數據成員。
指針
靜態成員函數
靜態成員函數和靜態數據成員同樣,它們都屬於類的靜態成員,它們都不是對象成員。所以,對靜態成員的引用不須要用對象名。
在靜態成員函數的實現中不能直接引用類中說明的非靜態成員,能夠引用類中說明的靜態成員。若是靜態成員函數中要引用非靜態成員時,可經過對象來引用。對象
下面看一個例子:
#include <iostream.h>
class Point
{
public:
void output()
{
}
static void init()
{
}
};
void main( void )
{
Point pt;
pt.init();
pt.output();
}
這樣編譯是不會有任何錯誤的。
下面這樣看
#include <iostream.h>
class Point
{
public:
void output()
{
}
static void init()
{
}
};
void main( void )
{
Point::output();
}
這樣編譯會處錯,錯誤信息:illegal call of non-static member function,爲何?
由於在沒有實例化一個類的具體對象時,類是沒有被分配內存空間的。
好的再看看下面的例子:
#include <iostream.h>
class Point
{
public:
void output()
{
}
static void init()
{
}
};
void main( void )
{
Point::init();
}
這時編譯就不會有錯誤,由於在類的定義時,它靜態數據和成員函數就有了它的內存區,它不屬於類的任何一個具體對象。
好的再看看下面的例子:
#include <iostream.h>
class Point
{
public:
void output()
{
}
static void init()
{
x = 0;
y = 0;
}
private:
int x;
int y;
};
void main( void )
{
Point::init();
}
編譯出錯:
illegal reference to data member ''Point::x'' in a static member function
illegal reference to data member ''Point::y'' in a static member function
在一個靜態成員函數裏錯誤的引用了數據成員,
仍是那個問題,靜態成員(函數),不屬於任何一個具體的對象,那麼在類的具體對象聲明以前就已經有了內存區,而如今非靜態數據成員尚未分配內存空間,那麼這裏調用就錯誤了,就好像沒有聲明一個變量卻提早使用它同樣。
也就是說在靜態成員函數中不能引用非靜態的成員變量。
好的再看看下面的例子:
#include <iostream.h>
class Point
{
public:
void output()
{
x = 0;
y = 0;
init();
}
static void init()
{
}
private:
int x;
int y;
};
void main( void )
{
Point::init();
}
好的,這樣就不會有任何錯誤。這最終仍是一個內存模型的問題,
任何變量在內存中有了本身的空間後,在其餘地方纔能被調用,不然就會出錯。
好的再看看下面的例子:
#include <iostream.h>
class Point
{
public:
void output()
{
}
static void init()
{
x = 0;
y = 0;
}
private:
static int x;
static int y;
};
void main( void )
{
Point::init();
}
編譯:
Linking...
test.obj : error LNK2001: unresolved external symbol "private: static int Point::y"
test.obj : error LNK2001: unresolved external symbol "private: static int Point::x"
Debug/Test.exe : fatal error LNK1120: 2 unresolved externals
執行 link.exe 時出錯.
能夠看到編譯沒有錯誤,鏈接錯誤,這又是爲何呢?
這是由於靜態的成員變量要進行初始化,能夠這樣:
#include <iostream.h>
class Point
{
public:
void output()
{
}
static void init()
{
x = 0;
y = 0;
}
private:
static int x;
static int y;
};
int Point::x = 0;
int Point::y = 0;
void main( void )
{
Point::init();
}
在靜態成員數據變量初始化以後就不會出現編譯錯誤了。
再看看下面的代碼:
#include <iostream.h>
class Point
{
public:
void output()
{
}
static void init()
{
x = 0;
y = 0;
}
private:
static int x;
static int y;
};
void main( void )
{
}
編譯沒有錯誤,爲何?
即便他們沒有初始化,由於咱們沒有訪問x,y,因此編譯不會出錯。
C++會區分兩種類型的成員函數:靜態成員函數和非靜態成員函數。這二者之間的一個重大區別是,靜態成員函數不接受隱含的this自變量。因此,它就沒法訪問本身類的非靜態成員。
在某些條件下,好比說在使用諸如pthread(它不支持類)此類的多線程庫時,就必須使用靜態的成員函數,由於其地址同C語言函數的地址兼容。這種銅限制就迫使程序員要利用各類解決辦法纔可以從靜態成員函數訪問到非靜態數據成員。
第一個解決辦法是聲明類的全部數據成員都是靜態的。運用這種方式的話,靜態的成員函數就可以直接地訪問它們,例如:
class Singleton
{
public:
static Singleton * instance();
private:
Singleton * p;
static Lock lock;
};
Singleton * Singleton::instance()
{
lock.getlock(); // fine, lock is static
if (!p)
p=new Singleton;
lock.unlock();
return p;
}
這種解決方法不適用於須要使用非靜態數據成員的類。
訪問非靜態數據成員
將參照傳遞給須要考量的對象可以讓靜態的成員函數訪問到對象的非靜態數據:
class A
{
public:
static void func(A & obj);
intgetval() const; //non-static member function
private:
intval;
};
靜態成員函數func()會使用參照obj來訪問非靜態成員val。
voidA::func(A & obj)
{
int n = obj.getval();
}
將一個參照或者指針做爲靜態成員函數的自變量傳遞,就是在模仿自動傳遞非靜態成員函數裏this自變量這一行爲。