1. static 变量在静态区,不管是局部静态还是全局静态,其生命周期与程序相同。从作用域和生命周期考虑问题分析。在内存分区的全局数据区分配内存,用时可以改变其值。
  2. static静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化; 静态局部变量一般在声明处初始化,如果没有显式初始化,会被程序自动初始化为0;始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,当定义它的函数或语句块结束时,其作用域随之结束;
  3. static 类成员变量,static类成员不像普通的类数据成员,static类数据成员独立于一切类对象存在。静态数据成员只分配一次内存。静态数据成员存储在全局数据区。static类数据成员是与类关联的,但不与该类定义的对象有任何关系。static类对象必须要在类外进行初始化。一般在.cpp 文件中初始化。而不是.h 文件。所有对象共享一个static类成员。
  4. 类的静态成员(变量和方法)属于类本身,在类加载的时候,就会分配内存,可以通过类名直接去访问;非静态成员(变量和方法)属于类的对象,所以只有在类的对象产生(创建类的实例)时才会分配内存,然后通过类的对象(实例)去访问。
  5. static:静态全局变量只在定义它的文件内有效,也在全局数据区,默认自动初始化为0。
  6. static 函数 1.出现在类内:静态类函数不会访问或者修改非static数据成员和成员函数,类的静态成员(变量和方法)属于类本身,而非对象的。也就是所有该类的对象共同拥有这一个成员函数,而不是普通的每个对象各自拥有一个成员函数 。因此static类成员函数是没有this指针的,this指针是指向本对象的指针,static类成员函数不能访问非static的类成员,只能访问 static修饰的类成员,同时静态成员函数不能申明为const。
  7. static 函数 1.出现在类外,用static修饰的函数,限定在本源码文件中,不能被本源码文件以外的代码文件调用。而普通的函数,默认是extern的,也就是说它可以被其它代码文件调用。
  8. 无论静态函数还是非静态函数,都是属于类的(这一点与数据成员的静态非静态不同),对象并不拥有函数的拷贝.两者的区别在于:非静态的函数由类对象(加.或指针加->;)调用,这时将向函数传递this指针.而静态函数由类名(::)(或对象名.)调用,但静态函数不传递this指针,不识别对象个体,所以通常用来对类的静态数据成员操作.

qt:

class name
{
public:
	name() {
		pn = this;
	}
	static int a;
	int m_b = 2;
	void func1();
	static int func2();
	static name *pn;
};
int name::a = 1;


void name::func1()
{
	int b = 1;
}

int name::func2()
{
	return pn->m_b;
}
name * name::pn = nullptr;
	name n;
	int a = name::func2();

进一步研究:

静态成员变量初始化的时间:

1.编译初始化

如果静态变量本身是基本数据类型(POD),且初始化值是常量,那么这个初始化过程是在编译期间完成的。

static int val = 10;
static char strArray[] = "hello world !";

2加载时初始化 

程序被加载时立即进行的初始化。这个初始化发生在main函数之前。即使程序任何地方都没访问过该变量, 仍然会进行初始化,因此形象地称之为"饿汉式初始化"。 
静态变量是一个基本数据类型,但是初始值非常量 
static int *p = new int[1024];

int x = 3;
int y = 4;
static int z = x + y;
12345 
静态变量是一个类对象,这种情况下即使是使用常量初始化,也是加载时初始化,而不是编译时初始化 
static std::string str = "Hello world !";

class MyClass {
public:    
    MyClass();    
    MyClass(int a, int b)
;};

static MyClass* MyClass1 = new MyClass();
static MyClass MyClass2;

3.运行时初始化 

这个初始化发生在变量第一次被引用。 也就是说,从程序执行模型角度看,程序所在进程空间中,哪个线程先访问了这个变量,就是哪个线程来初始化这个变量。因此,相对于加载初始化来说,这种初始化是把真正的初始化动作推迟到第一次被访问时,因而形象地称为"懒汉式初始化"。 
int myfunc()
{         
    static std::string msg = "hello world !";    //运行时初始化
}

总结理解:

1.全局的静态变量, 在运行main函数之前初始化。是饿汉式初始化。

2.局部静态变量,在运行到具体函数才初始化。是懒汉式初始化。


 

应用:回调函数

回调函数是基于C编程的Windows SDK的技术,不是针对C++的,程序员可以将一个C函数直接作为回调函数,但是如果试图直接使用C++的成员函数作为回调函数将发生错误,甚至编译就不能通过。
分析原因:
普通的C++成员函数都隐含了一个传递函数作为参数,亦即“this”指针,C++通过传递一个指向自身的指针给其成员函数从而实现程序函数可以访问C++的数据成员。这也可以理解为什么C++类的多个实例可以共享成员函数但是确有不同的数据成员。由于this指针的作用,使得将一个CALLBACK型的成员函数作为回调函数安装时就会因为隐含的this指针使得函数参数个数不匹配,从而导致回调函数安装失败。

解决办法:

1.不使用成员函数,直接使用普通C函数,为了实现在C函数中可以访问类的成员变量,可以使用友元操作符(friend),在C++中将该C函数说明为类的友元即可。这种处理机制与普通的C编程中使用回调函数一样。
2.使用静态成员函数,静态成员函数不使用this指针作为隐含参数,这样就可以作为回调函数了。

类的成员函数作为回调函数,要头文件类的成员函数声明处加上static,变成静态函数。表示该函数属于类调用,而不是类对象调用。静态成员函数具有两大特点:其一,可以在没有类实例的情况下使用;其二,只能访问静态成员变量和静态成员函数,不能访问非静态成员变量和非静态成员函数。

       由于在C++中使用类成员函数作为回调函数的目的就是为了访问所有的成员变量和成员函数,如果作不到这一点将不具有实际意义。解决的办法也很简单,就是使用一个静态类指针作为类成员,通过在类创建时初始化该静态指针,如m_pThis=this,然后在回调函数中通过该静态指针就可以访问所有成员变量和成员函数了。这种处理办法适用于只有一个类实例的情况。因为多个类实例将共享静态类成员和静态成员函数,这就导致静态指针指向最后创建的类实例。为了避免这种情况,可以通过附加参数(主要目的就是传递this指针)全局变量的方式的方式传递到静态成员函数中

参数传递的方式

#include <iostream.h>   

class MyClassq
{
public:
    MyClassq();
    ~MyClassq();

private:
public:
    static int ab;
    static int abcompare(void *pobj, int a, int b);
    int c = 2;
    static MyClassq *ps;
};
int MyClassq::ab = 2;

int MyClassq::abcompare(void *pobj, int a, int b)
{
    MyClassq *pt = (MyClassq*)pobj;
    pt->c = 1;
    if (a > b)
    {
        return 1;
    }
    else
        return 0;
}

MyClassq::MyClassq()
{
}

MyClassq::~MyClassq()
{
}

typedef int(*pt2Function)(void *,int, int);
pt2Function pt2tfun;
void DoItB(pt2Function pt2tfun, void *pobj)
{
    pt2tfun(pobj,1, 2);
}

int main()
{
    MyClassq qclass;
    MyClassq  *pt2Object = &qclass;
    DoItB(MyClassq::abcompare, pt2Object);

}

2,全局变量的方式
 

#include <iostream.h>   

void* pt2Object;    // 全局变量
class MyClassq
{
public:
    MyClassq();
    ~MyClassq();

private:
public:
    static int ab;
    static int abcompare(int a, int b);
    int c = 2;
    static MyClassq *ps;
};
int MyClassq::ab = 2;

int MyClassq::abcompare(int a, int b)
{
    MyClassq *pt = (MyClassq*)pt2Object;
    pt->c = 1;
    if (a > b)
    {
        return 1;
    }
    else
        return 0;
}

MyClassq::MyClassq()
{
}

MyClassq::~MyClassq()
{
}

typedef int(*pt2Function)(int, int);
pt2Function pt2tfun;
void DoItB(pt2Function pt2tfun)
{
    pt2tfun(1, 2);   
}

int main()
{
    MyClassq qclass;
    MyClassq *pt2Object = &qclass;
    DoItB(MyClassq::abcompare);

};

回调函数就是一个通过函数指针调用的函数,就是说把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。

如排序函数:qsort()函数,在第三个变量参数就是回调函数。

很多时候,注册的时候并不调用回调函数,而是在其他函数中调用,那我们可以定义一个CallbackFun全局指针变量,在注册的时候将函数指针赋给它,在要调用的调用它。

根据上述代码,C++中一下5中类型的初始化方法

1、静态变量,只能在类的定义外初始化,不能通过初始化列表初始化,不能通过构造函数初始化,不能在类的定义中初始化;

2、常量,只能通过初始化列表初始化,不能在类的定义外(内)初始化,不能通过构造函数初始化。

3、引用变量,只能通过初始化列表初始化且必须用变量初始化,不能在类的定义外(内)初始化,不能通过构造函数初始化。

4、静态整型常量,能在在类外初始化,不能带static,(整型)能否在类中初始化,取决于编译器

5、静态非整型常量,能在在类外初始化,不能带static,(double型)能否在类中初始化,取决于编译器
 

为什么有了常量之后还需要静态常量?
还记得静态成员存储的位置吗?不存储在类中,所有对象共享静态成员。
所以静态常量存在的最大意义就在于,创建多个对象时,节省内存空间。

静态函数的意义及作用

1.可以实现某些特殊的设计模式:如单例模式 Singleton;参考见:https://blog.csdn.net/baidu_16370559/article/details/122495451

2.由于没有this指针,可以把某些系统API的回调函数以静态函数的形式封装到类的内部

总之,从OOA/OOD的角度考虑,一切不需要实例化就可以有确定行为方式的函数都应该设计成静态的。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐