经过static修饰的变量,存储在内存的全局静态区。且被static修饰的变量只能在本模块的所有函数引用。
内存中的存储区域如下:
堆区:是由程序员手动申请(new)与释放(delete)的内存区域。从低地址向高地址申请;内存空间大、存储地址不连续,一般是链式的;速度较慢。
栈区:由编译器自动分配和释放,主要存储 函数的参数值、函数内部的变量的值、函数调用的空间。从高地址向低地址申请;容量有限;速度较快;存储地址连续,会溢出。
代码区:又叫文本段(.text),存放着程序的机器代码,可执行指令就是存储在这里的,这里的代码是只读的。
全局区(静态区):全局变量和静态变量是存储在这里的。初始化的全局变量和静态变量在一块区域(.data),未初始化的全局变量和未初始化的静态变量在相邻的另一块区域(.bbs)。系统结束后由系统释放。
常量区:常量字符串放在这里,程序结束后,由系统进行释放。
static的用法主要体现在两个方面:面向过程中的static和面向对象中的static。
面向过程的static主要包括静态全局变量、静态局部变量和静态函数。
面向对象的static主要包括静态成员变量、静态成员函数。
全局变量前添加static关键字,则该变量称为静态全局变量。
#include <iostream>
#include <stdio.h>
static int a = 10;
void Func()
{
a++;
}
int main()
{
printf("a = %d\n", a);//输出:10
Func();//输出:10
printf("a = %d\n", a);//输出:11
system("pause");
return 0;
}
特点:
1)在全局数据中的变量如果没有显示的初始化会自动被程序初始化为0(这个特性非静态全局变量也有),而在函数体内声明的变量如果不显示初始化则会使一个随机值;
2)静态全局变量在声明它的整个文件中都是可见的,而在文件之外是不可见的;
3)静态全局变量在全局数据区分配内存;
4)其他文件中可以定义同名int型变量a,不会冲突;
如果将static去掉,具有以下特点:
1)全局变量默认是有外部连接性的,其作用域是整个工程,在一个文件内定义的全局变量可以通过包含其所在头文件或显示调用 extern关键字修饰全局变量的变量名声明来引用;
2)静态全局变量是显示调用static修饰的全局变量,其作用域只在声明此变量的文件中,其他文件即使使用extern关键字修饰其声明也不可使用;
局部变量前添加static关键字,则该变量称为静态局部变量。
#include <iostream>
#include <stdio.h>
void Func()
{
static int a = 5;
printf("a = %d\n", a);
a++;
}
int main()
{
for (int i = 0; i < 5; i++)
{
Func(); //打印结果:5 6 7 8 9
}
system("pause");
return 0;
}
通常,在一个函数作用域内定义一个变量,每次运行到该函数时,系统会给局部变量分配内存。当函数结束时,该变量的内存会被系统回收至栈内存当中。
特点:
1)内存存放在程序的全局数据区中;
2)静态局部变量在程序执行到该对象声明时,会被首次初始化。其后运行到该对象的声明时,不会再次初始化,这也是为什么上面程序测试函数每次输出的值都是递增的原因(只会被初始化一次);
3)如果静态局部变量没有被显式初始化,则其值会自动被系统初始化为0;
4)局部静态变量不能被其作用域之外的其他模块调用,其调用范围仅限于声明该变量的函数作用域当中;
函数返回类型前添加static关键字,则该变量称为静态函数。
#include <iostream>
#include <stdio.h>
static void Func()
{
printf("This is a static function\n");
}
int main()
{
Func();
system("pause");
return 0;
}
特点:
1)作用域只在声明它的文件当中,不能被其他文件引用,其他文件可以定义同名的全局函数;
2)其他文件想要调用本文件的静态函数,需要显示的调用extern关键字修饰其声明;
#include <iostream>
#include <stdio.h>
using namespace std;
class Test
{
public:
Test(int a, int b, int c) :
m_a(a),
m_b(b),
m_c(c)
{
m = a + b + c;
}
void Show()
{
cout << "m = " << m << endl;
}
private:
int m_a, m_b, m_c;
static int m;
};
int Test::m = 0; //初始化静态数据成员
int main()
{
Test ClassA(1, 1, 1);
ClassA.Show(); //输出: 3
Test ClassB(3, 3, 3);
ClassB.Show(); //输出: 9
ClassA.Show(); //输出: 9
system("pause");
return 0;
}
特点:
1)静态数据成员的服务对象并非是单个类实例化的对象,而是所有类实例化的对象(这点可以用于设计模式中的单例模式实现);
2)静态数据成员必须显式的初始化分配内存,在其包含类没有任何实例化之前已经有内存分配;
3)静态数据成员与其他成员一样,遵从public,protected,private的访问规则;
4)静态数据成员内存存储在全局数据区,只随着进程的消亡而消亡;
优势:
1)静态数据成员不进入程序全局命名空间,不会与其他全局名称的同名同类型变量冲突;
2)静态数据成员可以实现C++的封装特性,由于其遵守类的访问权限规则,所以相比全局变量更加灵活;
类的成员函数返回类型之前添加static,此成员函数为静态成员函数。
#include <iostream>
#include <stdio.h>
using namespace std;
class Test
{
public:
Test(int a, int b, int c) :
m_a(a),
m_b(b),
m_c(c)
{
m = a + b + c;
}
static void Show()
{
cout << "m = " << m << endl;
}
private:
int m_a, m_b, m_c;
static int m;
};
int Test::m = 0; //初始化静态数据成员
int main()
{
Test ClassA(1, 1, 1);
ClassA.Show();
Test ClassB(3, 3, 3);
ClassB.Show();
ClassA.Show();
Test::Show(); //输出: 9
system("pause");
return 0;
}
特点:
1)静态成员函数比普通成员函数多了一种调用方式;
2)在没有实例化的类对象的条件下可以调用类的静态成员函数;
3)静态成员函数中没有隐含的this指针,所以静态成员函数不可以操作类中的非静态成员(由于第二条可知,类的非静态成员是在类实例化后存在的,而类的成员函数可以在类没有实例化的时候调用,故不能操作类的非静态成员);
1)静态数据成员都是静态存储的,所以必须在main函数之前显示的对其进行初始化;
2)可以在头文件中声明静态全局变量,该头文件被多个cpp文件包含后,包含该头文件的cpp文件实际上会各自拥有独立的同名变量;
3)不能将静态成员函数定义为虚函数;
4)静态成员函数没有this指针;
5)static缩短了子类对父类静态成员访问的时间,相对来说节省了内存空间;
6)如果不想在子类中操作父类的静态成员,则可以在子类中定义一个同名的static成员。这样既可覆盖父类中的静态成员,并且根据C++的多态性变量命名规则,这样做是安全的;
7)静态成员声明在类中,操作在其外部,所以对其取地址操作就跟取普通成员的操作略有不同。静态变量地址是指向其数据类型的指针,函数地址则是一个类型为nonmember的函数指针;