Home C++中静态成员及静态成员函数详解
Post
Cancel

C++中静态成员及静态成员函数详解

1. 局部静态变量

局部静态变量用于函数体内部修饰变量,这种变量的生存期长于该函数。

用法:局部变量前加static,修饰局部变量为静态局部变量。

作用:改变局部变量的销毁时期。

作用域:局部作用域,当定义它的函数或语句块结束的时候,作用域结束。

与局部变量的区别:

  1. 静态局部变量在全局数据区(静态存储区)分配内存(局部变量在栈区分配内存);
  2. 静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化(局部变量每次函数调用都会被初始化);
  3. 静态局部变量一般在声明处初始化,如果没有显式初始化,会被程序自动初始化为0(局部变量不会被初始化);
  4. 它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,也就是不能在函数体外面使用它(局部变量在栈区,在函数结束后立即释放内存);
  5. 当静态局部变量离开作用域后,并没有被销毁。当该函数再次被调用的时候,该变量的值为上次函数调用结束时的值(局部变量离开作用域便被销毁,再次调用函数时,其值为初始值)。

与全局变量的区别:

同样是初始化一次,连续调用fun()的结果是一样的,但是,使用全局变量的话,变量就不属于函数本身了,不再仅受函数的控制,给程序的维护带来不便。

静态局部变量正好可以解决这个问题。静态局部变量保存在全局数据区,而不是保存在栈中,每次的值保持到下一次调用,直到下次赋新值。

Code:

1
2
3
4
5
6
7
8
9
10
11
#include<stdio.h>
int fun(void){
	static int count = 20;      // 只能在该函数内修改静态局部变量的值
	return count--;
}

int main() {
	printf("global\t\tlocal static\n");
	int count = 1;
	for(; count <= 10; ++count) printf("%d\t\t%d\n", count, fun());
}

运行结果图

2. 全局静态变量

全局静态变量定义在函数体外,该变量只在本文件可见。

用法:全局变量前加static,修饰全局变量为静态全局变量。

作用:改变全局变量的可见性。静态全局变量的存储位置在静态存储区,未被初始化的静态全局变量会被自动初始化为0。

作用域:全局静态变量在声明他的文件之外是不可见的,仅在从定义该变量的开始位置到文件结尾可见。

特点:

  • 静态全局变量不能被其它文件所用(全局变量可以);
  • 其它文件中可以定义相同名字的变量,不会发生冲突。

Code:

以下两个文件要放到项目工程里才有效果。

1
2
3
4
//file a.cpp
 
//static int n = 15; // ERROR!因为static隔离了文件。
int n = 15;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//file main.cpp

#include <iostream>
//#include"a.cpp"       //不管有没有static,加include都不会报错
using namespace std;

extern int n;

void fn(){
	n++;
}

int main() {
	cout<<"Before:" <<n<<endl;
	fn();
	cout<<"After:" <<n<<endl;
	return 0;
}

运行结果图

3. 静态函数

静态函数的作用与2. 静态全局变量类似。特点也类似:不能被其他文件所用;其它文件中可以定义相同名字的变量,不会发生冲突。

用法:函数返回类型前加static,修饰函数为静态函数。

作用:改变函数的可见性。函数的定义和声明在默认情况下都是extern的,但静态函数只在声明它的文件中可见,不能被其他文件使用。

4. 静态成员

用法:类成员前加static,修饰类的成员为类的静态成员。

作用:实现多个对象之间的数据共享,并且使用静态成员不会破坏封装性,也保证了安全性。

Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include<iostream>
using namespace std;

class Rectangle
{
private:
    int m_w,m_h;
    static int s_sum;

public:
    Rectangle(int w,int h)
    {
        this->m_w = w;
        this->m_h = h;
        s_sum += (this->m_w * this->m_h);
    }

    void GetSum()
    {
        cout<<"sum = "<<s_sum<<endl;
    }
};

int Rectangle::s_sum = 0;  //静态成员类外初始化,因为其属于类,不属于类对象

int main()
{
    cout<<"sizeof(Rectangle)="<<sizeof(Rectangle)<<endl;
    Rectangle *rect1 = new Rectangle(3,4);
    rect1->GetSum();
    cout<<"sizeof(rect1)="<<sizeof(*rect1)<<endl;
    Rectangle rect2(2,3);
    rect2.GetSum();
    cout<<"sizeof(rect2)="<<sizeof(rect2)<<endl;
}
1
2
3
4
5
sizeof(Rectangle)=8
sum = 12
sizeof(rect1)=8
sum = 12
sizeof(rect1)=8

由此可知:sizeof(Rectangle) = 8bytes = sizeof(m_w) + sizeof(m_h)。也就是说静态数据成员并不占用Rectangle的内存空间。因为静态数据成员在全局数据区(静态区)分配内存。再看看GetSum(),第一次12 = 3 * 4,第二次18 = 12 + 2 * 3。因此,静态数据成员只会被初始化一次,与对象无关。

结论:

静态数据成员被当作是类的成员,由该类型的所有对象共享访问,对该类的多个对象来说,静态数据成员只分配一次内存。静态数据成员存储在全局数据区。静态数据成员定义时要分配空间,所以不能在类声明中定义。从某种程度上来说,静态数据成员与静态变量相类似。

5. 静态成员函数

用法:类函数前加static,修饰类的函数为静态函数。

作用:减少资源消耗,不需要实例化就可以使用

Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include<iostream>
using namespace std;

class Rectangle
{
private:
    int m_w,m_h;
    static int s_sum;

public:
    Rectangle(int w,int h)
    {
        this->m_w = w;
        this->m_h = h;
        s_sum += (this->m_w * this->m_h);
    }

    static void GetSum()                // 此处为静态成员函数
    {
        cout<<"sum = "<<s_sum<<endl;
    }
};

int Rectangle::s_sum = 0;  //静态成员类外初始化,因为其属于类,不属于类对象

int main()
{
    cout<<"sizeof(Rectangle)="<<sizeof(Rectangle)<<endl;
    Rectangle *rect1 = new Rectangle(3,4);
    rect1->GetSum();
    cout<<"sizeof(rect1)="<<sizeof(*rect1)<<endl;
    Rectangle rect2(2,3);
    rect2.GetSum();          //可以用对象名.函数名访问
    cout<<"sizeof(rect2)="<<sizeof(rect2)<<endl;
    Rectangle::GetSum();     //也可以可以用类名::函数名访问
}

结论:

  • 非静态成员函数可访问静态成员函数/成员;

  • 静态成员函数不能访问非静态成员函数/成员,只能访问静态成员函数/变量;

  • 调用静态成员函数,可以用成员访问操作符(.)和(->)为一个类的对象或指向类对象的指针调用静态成员函数,也可以用类名::函数名调用。

另外,既然是在类里操作,对类成员的访问还是要遵从publicprotectedprivate访问规则。

6. 静态变量内存分配和初始化

全局变量文件域的静态变量类的静态成员变量在main执行之前的静态初始化过程中分配内存并初始化;局部静态变量(一般为函数内的静态变量)在第一次使用时分配内存并初始化。这里的变量包含内置数据类型和自定义类型的对象。

7. static关键字的好处

7.1 隐藏变量或函数、隔离错误,有利于模块化程序

在编程中,难免会用到全局变量,全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,全局变量在所有的源文件中都是有效的。如果希望全局变量仅限于在本源文件中使用,在其他源文件中不能引用,也就是说限制其作用域只在定义该变量的源文件内有效,而在同一源程序的其他源文件中不能使用,这时,就可以通过在全局变量上加static来实现,使全局变量被定义成一个静态全局变量。这样就可以避免其他源文件使用该变量、避免其他源文件因为该变量引起的错误。起到了对其他源文件隐藏该变量和隔离错误的作用,有利于模块化程序。

7.2 保持变量内容的持久性

有时候,我们希望函数中局部变量的值在函数调用结束之后不会消失,仍然保留函数调用结束的值。即它所在的存储单元不释放。这时,应该将该局部变量用关关键字static声明为静态局部变量。当局部变量被声明为静态局部变量的时候,也就改变了局部变量的存储位置,从原来的栈中存放改为静态存储区存放,全局变量也存放在静态存储区,静态局部变量与全局变量的主要区别就在于可见性,静态局部变量只在其被声明的代码块中是可见的。

This post is licensed under CC BY 4.0 by the author.

C++中vector的初始化及赋值方式

Difference between NEW and MALLOC in C++