對下面文章的總結:
1.對於靜態變數而言:在地化(名字衝突)、初始化=0、唯一共享性(靜態區) 。特別地,對於類別靜態成員變數:(1)屬於整個類,可以直接透過類別名稱存取而不用透過實例(2)必須初始化,類別內static聲明,類別外初始化(不可以再加static)
2.對於類別靜態成員函數而言,(1)沒有this指針,僅能存取靜態成員變數和靜態成員函數,不能宣告為虛函數(2)常用於多執行緒中的子類別。
-------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---
1、什麼是static?
static 是C++中很常用的修飾符,它被用來控制變數的儲存方式和可見性。
2.為什麼要引入static?
函數內部定義的變數,當程式執行到它的定義處時,編譯器為它在堆疊上分配空間,大家知道,函數在堆疊上分配的空間在此函數執行結束時會釋放掉,這樣就產生了一個問題: 如果想將函數中此變數的值保存至下一次呼叫時,如何實現? 最容易想到的方法是定義一個全域的變量,但定義為一個全域變數有許多缺點,最明顯的缺點是破壞了此變數的存取範圍(使得在此函數中定義的變量,不僅僅受此函數控制)。
3、何時用static?
需要一個資料物件為整個類別而非某個物件服務,同時又力求不破壞類別的封裝性,即要求此成員隱藏在類別中的內部,對外不可見。
4、static的內部機制:
靜態資料成員在程式一開始運作時必須存在。因為函數在程式運行中被調用,所以靜態資料成員不能在任何函數內分配空間和初始化。
這樣,它的空間分配有三個可能的地方,一是作為類的外部接口的頭文件,那裡有類聲明;二是類定義的內部實現,那裡有類的成員函數定義;三是應用程式的main()函數前的全域資料宣告與定義處。
靜態資料成員要實際分配空間,因此不能定義在類別的宣告中(只能宣告資料成員)。類別聲明只聲明一個類別的“尺寸和規格”,並沒有進行實際的記憶體分配,所以在類別聲明中寫成定義是錯誤的。它也不能在頭檔中類別聲明的外部定義,因為那會造成在多個使用該類別的來源檔案中,對其重複定義。
static被引入以告知編譯器,將變數儲存在程式的靜態儲存區而非堆疊上空間,靜態
資料成員依定義出現的先後順序依序初始化,注意靜態成員巢狀時,要保證所嵌套的成員已經初始化了。消除時的順序是初始化的反順序。
5、static的優點:
可節省內存,因為它是所有物件所公有的,因此,對於多個物件來說,靜態資料成員只儲存一處,供所有物件共用。靜態資料成員的值對每個物件都是一樣,但它的值是可以更新的。只要對靜態資料成員的值更新一次,確保所有物件存取更新後的相同的值,這樣可以提高時間效率。
6、引用靜態資料成員時,採用下列格式:
<#類別名稱>::<靜態成員名稱>
7、注意事項:
(1)類別的靜態成員函數是屬於整個類別而非類別的物件,所以它沒有this指針,這就導致了它僅能存取類別的靜態資料和靜態成員函數。
(2)無法定義靜態成員函數為虛函數。
(3)由於靜態成員宣告於類別中,操作於其外,所以對其取位址操作,就多少有些特殊,變數位址是指向其資料類型的指針,函數位址類型是一個「nonmember函數指標」。
(4)由於靜態成員函數沒有this指針,所以就差不多等同於nonmember函數,結果就產生了一個意想不到的好處:成為一個callback函數,使得我們得以將C++和C-based X Window系統結合,同時也成功的應用於執行緒函數身上。
(5)static並沒有增加程式的時空開銷,相反她也縮短了子類別對父類別靜態成員的存取時間,節省了子類別的記憶體空間。
(6)靜態資料成員在<定義或說明>時前面加關鍵字static。
(7)靜態資料成員是靜態儲存的,所以必須對它進行初始化。
(8)靜態成員初始化與一般資料成員初始化不同:
初始化在類體外進行,而前面不加static
#,以免與一般靜態變數或物件混淆;
初始化時不加該成員的存取權控制符private,public等; 初始化時使用作用域 所以成員初始化的格式:
<資料型別><類別名稱>::<靜態資料成員名稱>=<值>
(9)為了防止父類別的影響,可以在子類別中定義一個與父類別相同的靜態變量,以屏蔽父類別的影響。這裡有一點要注意:我們說靜態成員為父類別和子類別共享,但我們有重複定義了靜態成員,這會不會造成錯誤呢?不會,我們的編譯器採用了絕妙的手法:name-mangling 用來產生唯一的標誌。
靜態資料成員
在類別中,靜態成員可以實現多個物件之間的資料共享,並且使用靜態資料成員還不會破壞隱藏的原則,即保證了安全性。因此,靜態成員是類別的所有物件中共享的成員,而不是某個物件的成員。
使用靜態資料成員可以節省內存,因為它是所有物件所公有的,因此,對多個物件來說,靜態資料成員只儲存一處,供所有物件共用。靜態資料成員的值對每個物件都是一樣,但它的值是可以更新的。只要對靜態資料成員的值更新一次,確保所有物件存取更新後的相同的值,這樣可以提高時間效率。
靜態資料成員的使用方法與注意事項如下:
1、靜態資料成員在定義或說明時前面加關鍵字static。
2、靜態成員初始化與一般資料成員初始化不同。靜態資料成員初始化的格式如下:
<資料型別><類別名稱>::<靜態資料成員名稱>=<值>
# 這表示:
(1) 初始化在類別體外進行,而前面不加static,以免與一般靜態變數或物件混淆。
(2) 初始化時不加該成員的存取權限控制符private,public等。
(3) 初始化時使用作用域運算子來標示它所屬類,因此,靜態資料成員是類別的成員,而不是物件的成員。
3、靜態資料成員是靜態儲存的,它是靜態生存期,必須對它進行初始化。
4、引用靜態資料成員時,採用以下格式:
<類別名稱>::<靜態成員名>
如果靜態資料成員的存取權限允許的話(即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自变量这一行为。
以上是php中static關鍵字的作用的詳細內容。更多資訊請關注PHP中文網其他相關文章!