【簡介】
1.靜態數據成員在類中聲明,在源文件中定義並初始化;
2.靜態成員函數沒有this指針,只能訪問靜態數據成員;
3.調用靜態成員函數:(1)對象、(2)直接調用;
4.靜態成員函數的地址可用普通函數指針儲存,可作為回調函數的參數。
【1.靜態數據成員】
1.靜態數據成員與全局變量一樣都是靜態分配存儲空間的,在編譯時,就要為類的靜態數據成員分配存儲空間。但全局變量在程序中的任何位置都可以訪問它,而靜態數據成員受到訪問權限的約束。必須是public權限時,才可能在類外進行訪問。
2.靜態數據成員的初始化
(1)*靜態數據成員的初始化是在類的源文件(.cpp)中,而不是在類的頭文件(.h)中進行的。這是因為類聲明位於頭文件中,程序可能將頭文件包括在其他幾個文件中。如果在頭文件中進行初始化,將出現多個初始化語句副本,從而引發錯誤。
A.h文件 class A { private: static int a; }; A.cpp文件 int A::a = 0; //數據類型 類名::靜態數據成員名 = 初值。
(2)因為靜態成員屬於整個類,而不屬於某個對象,如果在類內初始化,會導致每個對象都包含該靜態成員。
(3)靜態成員變量在類中僅僅是聲明(聲明只是表明了變量的數據類型和屬性,並不分配內存),沒有定義,所以要在類的外面定義(定義是給靜態成員變量分配內存)。
class A { public: static int a; //聲明但未定義 }; int main() { printf("%d", A::a); //error。 a沒分配內存,不能訪問。 return 0; } class A { public: static int a; //聲明但未定義 }; int A::a = 3; //定義了靜態成員變量,同時初始化。也可以寫"int A:a;",即不給初值,同樣可以通過編譯。 int main() { printf("%d", A::a);//ok。a分配了內存,可以訪問。 return 0; }
(4)注意:靜態數據成員在類聲明中聲明,在包含類方法的文件中初始化。
3.靜態成員能在類的范圍內共享。在類中,靜態成員可以實現多個對象之間的數據共享。它的值是可以更新的。只要對靜態數據成員的值更新一次,保證所有對象存取更新后的相同的值。
4.類的靜態成員是可以獨立訪問的,也就是說,不需要創建類的實例就可以訪問靜態成員。
5.派生類對象與基類對象共享基類的靜態數據成員。
class base { public: static int _num; //聲明靜態成員 }; int base::_num = 0; //靜態數據成員的真正定義 class derived : public base { }; main() { base a; derived b; a._num++; cout << a._num << endl; b._num++; cout << b._num << endl; cout << a._num << endl; } 運行結果:1 2 2
6.靜態數據成員的類型可以是所屬類的類型,而普通數據成員則不可以。普通數據成員的只能聲明為所屬類類型的指針或引用。
class base { public: static base a;//正確,靜態數據成員 base b;//錯誤 base *p;//正確,指針 base &m;//正確,引用 };
7.靜態數據成員的值在const成員函數中可以被合法的改變。
base.h文件 class base { public: base() { i = 0; } private: static int a; int i; void test() const //const 成員函數 { a++;//正確,static數據成員 i++;//錯誤 } }; base.cpp文件 int base::a = 0;
【2.靜態成員函數】
1.靜態函數是使用 static 修飾符修飾的函數,靜態函數沒有 this 指針,只能訪問靜態成員。
2.調用靜態成員函數(只能訪問靜態成員)
(1)對象可調用靜態成員函數
(2)可直接調用靜態成員函數
class Obj { static int i; public: Obj() { i++; cout << ’a’; } ~Obj() { i--; cout << ’b’; } static int getVal() { return i; } //靜態成員函數(只能訪問靜態成員) }; int Obj::i = 0; //靜態成員初始化 void f() { Obj ob2; cout << ob2.getVal(); } //1.對象可調用靜態成員函數 void main() { Obj ob1; f(); Obj *ob3 = new Obj; //new新建一個對象,再將該對象的指針賦值給指針ob3 cout << ob3->getVal(); delete ob3; cout << Obj::getVal(); //2.可直接調用靜態成員函數 輸出:aa2ba2b1b }
3.在類中如果函數調用的結果不會訪問或者修改任何對象數據成員,這樣的成員聲明為靜態成員函數比較好。
4.類的靜態成員函數可以訪問類的私有成員,但是靜態成員函數只能直接訪問類的靜態私有成員,因為靜態成員函數是不可以直接訪問非靜態的成員的。
5.靜態成員函數可以借助對象名和指針來訪問類的非靜態私有成員。
class DATA { private: int i; //非靜態私有成員 static int j; //靜態數據成員 public: DATA(int num) { i = num; j += num; } static show(DATA c) { cout << ”i = ” << c.i << ”, j = ” << j << endl; //非靜態成員i(用對象名來引用);靜態成員(直接引用)。 } }; int DATA::j = 2; void main() { DATA a(2), b(4); DATA::show(a); DATA::show(b); } 輸出: i = 2, j = 8 i = 4, j = 8
6.不能把靜態成員函數定義為虛函數。靜態成員函數也是在編譯時分配存儲空間,所以在程序的執行過程中不能提供多態性。
7.*靜態成員函數的地址可用普通函數指針儲存,而普通成員函數地址需要用類成員函數指針來儲存。
base.h文件 class base { public: static int func1(); int func2(); }; base.cpp文件 main() { int(*pf1)() = &base::func1; int (base::*pf2)() = &base::func2; }
真實案例:
DDPlatform.h文件 /* 登陸狀態回調 ulState: 當前登陸狀態 ulUserHandle: 登陸成功后的用戶句柄,ucState==LOGIN_SUCCEED時值有效 ulALCHandle: 報警服務器句柄,ucState==LOGIN_SUCCEED時值有效 */ typedef void (CALLBACK *fLoginStateCallback)(ULONG ulState, ULONG ulUserHandle, ULONG ulALCHandle); /* 設備狀態改變回調 ulCameraID: 設備ID ulState: 當前狀態 ulUserHandle: 登陸用戶句柄 ulALCHandle: 報警服務器句柄 */ typedef void (CALLBACK *fCameraRestateCallback)(ULONG ulCameraID, ULONG ulState, ULONG ulUserHandle, ULONG ulALCHandle); struct PE_REGCALLBACK { fLoginStateCallback cbLoginState; fCameraRestateCallback cbCameraRestate; }; AlarmSystemWindow.h文件 #pragma once #include "DDPlatform.h" #include <BaseWidget.h> class AlarmSystemWindow : public BaseWidget { Q_OBJECT public: AlarmSystemWindow(QWidget *parent); ~AlarmSystemWindow(); public: static void CALLBACK LoginState(ULONG ulState, ULONG ulUserHandle, ULONG ulALCHandle); static void CALLBACK CameraRestate(ULONG ulCameraID, ULONG ulState, ULONG ulUserHandle, ULONG ulALCHandle); private: PE_REGCALLBACK m_cbRegister; priate: void init(); }; AlarmSystemWindow.cpp文件 void AlarmSystemWindow::init() { memset(&m_cbRegister, 0, sizeof(m_cbRegister)); m_cbRegister.cbLoginState = LoginState; //靜態成員函數的地址可用普通函數指針儲存 m_cbRegister.cbCameraRestate = CameraRestate; bool RegisterCallback = DDPlatform::DDPlat_RegisterCallback(m_cbRegister); } void CALLBACK AlarmSystemWindow::LoginState(ULONG ulState, ULONG ulUserHandle, ULONG ulALCHandle) { AlarmSystemWindow* pThat = (AlarmSystemWindow*)g_AlarmWindow; if (ulState == LOGIN_SUCCEED) { pThat->g_ulLoingUserHandle = ulUserHandle; pThat->sglSendLoginHandle(ulUserHandle); //發送登錄句柄 } else if (ulState == LOGIN_QUERERR || ulState == LOGIN_CONNERR || ulState == LOGIN_LOGINERR || ulState == LOGIN_AUTHFAIL) { } } void CALLBACK AlarmSystemWindow::CameraRestate(ULONG ulCameraID, ULONG ulState, ULONG ulUserHandle, ULONG ulALCHandle) { AlarmSystemWindow* pThat = (AlarmSystemWindow*)g_AlarmWindow; //獲取設備報警狀態 if (ulState == CAMERAST_ALARMING) { //報警中 pThat->sglSendAlarmDeviceData(ulCameraID); } }
注意:回調函數是將一個函數的指針作為另一個函數的參數,當另一個函數執行完后再執行該函數。
博客園的這個文本編輯實在是太難搞了,就這樣吧...強迫症的我也屈服了