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

 

 

注意:回调函数是将一个函数的指针作为另一个函数的参数,当另一个函数执行完后再执行该函数

 

 

博客园的这个文本编辑实在是太难搞了,就这样吧...强迫症的我也屈服了

 

 

posted @ 2019-12-03 16:34  远方是什么样子  阅读(1651)  评论(0编辑  收藏  举报