C++基礎(靜態數據成員和靜態成員函數)


 

簡介

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);
    }
}

 

 

注意:回調函數是將一個函數的指針作為另一個函數的參數,當另一個函數執行完后再執行該函數

 

 

博客園的這個文本編輯實在是太難搞了,就這樣吧...強迫症的我也屈服了

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM