Jamzy Wang

life is a struggle,be willing to do,be happy to bear~~~

C/C++ 中的static关键字

2012-06-28 21:23

原创声明:本作品采用知识共享署名-非商业性使用 3.0 版本许可协议进行许可,欢迎转载,演绎,但是必须保留本文的署名(包含链接),且不得用于商业目的。

static 关键字可用于声明变量、函数、类数据成员和类函数。

默认情况下,在所有块的外部定义的对象或变量具有静态持续时间和外部链接。静态持续时间意味着,在程序启动时分配对象或变量,并在程序结束时释放对象或变量。外部链接意味着,变量的名称在用于声明变量的文件的外部是可见的。 相反,内部链接意味着,名称在用于声明变量的文件的外部是不可见的。在以下情况下,可使用 static 关键字:

1) 在文件范围(全局和/或命名空间范围)内声明变量或函数时,static关键字将指定变量或函数具有内部链接。

2) 在声明变量时,变量具有静态持续时间,并且除非您指定另一个值,否则编译器会将变量初始化为 0。

3) 在函数中声明变量时,static 关键字将指定变量将在对该函数的调用中保持其状态。

4) 在类声明中声明数据成员时,static 关键字将指定类的所有实例共享该成员的一个副本。

5) 必须在文件范围内定义静态数据成员。

6) 在类声明中声明成员函数时,static 关键字将指定类的所有实例共享该函数。

7) 由于函数没有隐式 this 指针,因此静态成员函数不能访问实例成员。

8) 不能将联合成员声明为静态的。 但是,全局声明的匿名联合必须是显式声明的 static。

static用来控制变量和函数的存储方式和可见性,它可以修饰局部变量、全局变量、函数,C++中static又增加了两种用法:静态数据成员、静态成员函数。static修饰的变量都存在内存的静态存储区中。

static修饰变量

静态全局变量

静态全局变量,作用域仅限于变量被定义的文件中,其他文件即使用extern 声明也没法使用他。准确地说作用域是从定义之处开始,到文件结尾处结束,在定义之处前面的那些 代码行也不能使用它。

1
2
3
4
5
static int j;
int main(int argc, char const *argv[]) {
    ...
    return 0;
}

## 静态局部变量

函数内部定义的变量,在程序执行到它的定义处时,编译器为它在栈上分配空间,函数在栈上分配的空间在此函数执行结束时会释放掉,这样就产生了一个问题: 如果想将函数中此变量的值保存至下一次调用时,如何实现? 最容易想到的方法是定义一个全局的变量,但定义为一个全局变量有许多缺点,最明显的缺点是破坏了此变量的访问范围(使得在此函数中定义的变量,不仅仅受此函数控制)。

静态局部变量,在函数体里面定义的,就只能在这个函数里用了,同一个文档中的其他函数也用不了。由于被static 修饰的变量总是存在内存的静态区,所以即使这个函数运行结束,这个静态变量的值还是不会被销毁,函数下次使用时仍然能用到这个值。

1
2
3
4
5
6
7
8
9
10
11
12
void funvoid
{
    static int i = 0;
    i++;
}
int main(int argc, char const *argv[]) {
    ...
    fun();
    fun();
    ...
    return 0;
}

static修饰函数

函数前加static 使得函数成为静态函数。但此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件(所以又称内部函数)。使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名。

1
2
3
4
5
6
7
8
9
10
11
static void funvoid
{
    static int i = 0;
    i++;
}
int main(int argc, char const *argv[]) {
    ...
    fun();
    ...
    return 0;
}

C++ 中的static

静态成员变量

一般情况下,如果一个类有许多个对象,那么每一个对象都分别有自己的成员变量,不同对象的成员变量各自有值,互不相干。但是有时我们希望有某一个或几个成员变量为所有对象共有,这样可以实现数据共享。

可以使用全局变量来达到共享数据的目的。例如在一个程序文件中有多个函数,每一个函数都可以改变全局变量的值,全局变量的值为各函数共享。但是用全局变量的安全性得不到保证,由于在各处都可以自由地修改全局变量的值,很有可能偶然失误,全局变量的值就被修改,导致程序的失败。因此在实际开发中很少使用全局变量。

如果想在同类的多个对象之间实现数据共享,也不要用全局变量,那么可以使用静态成员变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class A
{
public:
    static int static_var;
    int var;
};
int A::static_var;   //初始化(编译器会给static变量自动赋值为0)
//int A::static_var = 4;   //初始化同时赋值
//不初始化:链接错误    
int main(int argc, char const *argv[]) {
    A a;
    cout << A::static_var << endl;
    cout << a.static_var << endl;
    return 0;
}

静态数据成员在<定义或说明>时前面加关键字static。静态数据成员是静态存储的,所以必须对它进行初始化。静态成员初始化与一般数据成员初始化不同:

  • 初始化在类体外进行,而前面不加static,以免与一般静态变量或对象相混淆;

  • 初始化时不加该成员的访问权限控制符private,public等;

  • 初始化时使用作用域运算符来标明它所属类;

所以我们得出静态数据成员初始化的格式: <数据类型><类名>::<静态数据成员名>=<值>,如 int A::static_var = 4;

static 成员变量属于类,不属于某个具体的对象,这就意味着,即使创建多个对象,也只为 A::static_var 分配一份内存,所有对象使用的都是这份内存中的数据。当某个对象修改了 A::static_var,也会影响到其他对象。

static 成员变量必须先初始化才能使用,否则链接错误。

1
2
1) int A::static_var;   //初始化(编译器会给static变量自动赋值为0)
2) int A::static_var = 4;   //初始化同时赋值

注意:static 成员变量的内存空间既不是在声明类时分配,也不是在创建对象时分配,而是在初始化时分配。static 成员变量与对象无关,不占用对象的内存,而是在所有对象之外开辟内存,即使不创建对象也可以访问。

static 成员变量既可以通过对象来访问,也可以通过类来访问。通过类来访问的形式为:类名::成员变量; 例如:

1
2
1) A a; cout << A::static_var << endl; //通过类名访问
2) cout << a.static_var << endl;    //通过对象来访问

上述这两种方式是等效的。

总结一下,静态成员变量有如下特性:

1) 一个类中可以有一个或多个静态成员变量,所有的对象都共享这些静态成员变量,都可以引用它。

2) static 成员变量和普通 static变量一样,编译时在静态数据区分配内存,到程序结束时才释放。

3) 静态成员变量必须初始化,而且只能在类体外进行。初始化时可以赋初值,也可以不赋值。如果不赋值,那么会被默认初始化,一般是 0。

4) 静态成员变量既可以通过对象名访问,也可以通过类名访问,但要遵循 private、protected 和 public 关键字的访问权限限制。

静态成员函数

在类中,static 除了声明静态成员变量,还可以声明静态成员函数,普通成员函数可以访问所有成员变量,而静态成员函数只能访问静态成员变量。静态成员函数可以直接引用本类中的静态数据成员,因为静态成员同样是属于类的,可以直接引用。在C++程序中,静态成员函数主要用来访问静态数据成员,而不访问非静态成员。

当调用一个对象的成员函数(非静态成员函数)时,系统会把当前对象的起始地址赋给 this 指针。而静态成员函数并不属于某一对象,它与任何对象都无关,因此静态成员函数没有 this 指针。既然它没有指向某一对象,就无法对该对象中的非静态成员进行访问。

可以说,静态成员函数与非静态成员函数的根本区别是:非静态成员函数有 this 指针,而静态成员函数没有 this 指针。由此决定了静态成员函数不能访问本类中的非静态成员。

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
class A
{
public:
    static int static_var;
    int var;
    A(int a)
    {
        var = a;
    };
    static void print_1()
    {
        cout << static_var << endl;
    }
    static void print_2()
    {
        cout << var << endl;//不能访问非静态成员变量
    }
    void print_3()
    {
        cout << static_var << endl;
        cout << var << endl;
    }
};
int A::static_var;
int main(int argc, char const *argv[]) {
    A a(10);
    A::print_1();
    A::print_2();//报错
    a.print_1();
    a.print_3();
    return 0;
}

与数据成员类似, 在类中声明函数的前面加static就成了静态成员函数。如

1
2
3
4
5
6
7
8
9
class A
{
...
 static void print_1()
{
   cout << static_var << endl;
}
...
}

如果要在类外调用 public 属性的静态成员函数,要用类名和域解析符“::”。如:A::print_1()。当然也可以通过对象名调用静态成员函数,如: a.print_1();

总结一下: 1) 静态成员函数中不能调用非静态成员。

2) 非静态成员函数中可以调用静态成员。

3) 静态成员变量使用前必须先初始化(如int MyClass::m_nNumber = 0;),否则会在linker时出错。

注意:

1) 它们不能声明为 virtual。

2) 它们不能与具有相同参数类型的非静态函数同名。

Ref

Comments