代码改变世界

C/C++ 中的static

2013-10-24 19:10  Ross Wang  阅读(316)  评论(0编辑  收藏  举报

首先,C语言和C++语言中面向过程编程方式下的static关键字用法基本相同,C++中static还可被用于面向对象的编程中,即类的静态成员,包括静态变量数据成员,静态常量数据成员和静态成员方法。

 

C语言和C++语言中面向过程编程方式下的static: 静态全局变量,静态全局常量,静态局部变量,静态局部常量和静态方法

静态全局变量和静态全局常量:用static关键字修饰的全局变量和全局常量。

#include<iostream>
#include<string>
using namespace std;
void PrintOutput(const string output)
{
    cout<<output<<endl;
}
class A
{
public:
    A(){PrintOutput("An A instance is initializing...");}
    ~A(){PrintOutput("An A instance is being destroyed...");}
};
class B
{
public:
    B(){PrintOutput("An B instance is initializing...");}
    ~B(){PrintOutput("An B instance is being destroyed...");}
};
static A a;
static const B b;
static int siVar;
int main()
{
    cout<<siVar<<endl;
    return 0;
}

上面代码中,siVar就是一个静态全局变量,b是一个静态全局常量。在程序启动后,进入main函数之前被初始化并分别存储在静态存储区和常量存储区,一直到程序退出时被销毁, 如果有多个静态全局变量或者静态全局常量,这时会按照和初始化顺序相反的顺序进行销毁。如果是类实例,会调用该类的析构函数。其实对于全局静态常量来说,就算不用static修饰,默认的作用域也是file scope,就是当前源文件,但是全局常量可以使用extern关键字在其他源文件中声明后使用。

程序输出为:

An A instance is initializing...
An B instance is initializing...
0
An B instance is being destroyed...
An A instance is being destroyed...

可以看到静态全局变量a和静态全局常量b都是在进入main函数之前就通过它们各自的默认构造函数初始化了,而程序结束时销毁这两个对象的顺序和初始化他们的顺序正好相反。

 

静态局部变量和静态局部常量:用static关键字修饰的局部变量和局部常量

#include<iostream>
#include<string>
using namespace std;
void PrintOutput(const string output)
{
    cout<<output<<endl;
}
class A
{
public:
    A(){PrintOutput("An A instance is initializing...");}
    ~A(){PrintOutput("An A instance is being destroyed...");}
};
class B
{
public:
    B(){PrintOutput("An B instance is initializing...");}
    ~B(){PrintOutput("An B instance is being destroyed...");}
};
void func()
{
    static int siVar;
    cout<<++siVar<<endl;
    static A a;
    static const B cb;
}
int main()
{
    cout<<"Entered main()"<<endl;;
    func();
    func();
    func();
    return 0;
}

上面代码中siVar就是一个静态局部变量,在函数func第一次被调用时初始化赋值为0,前自增后输出,以后每次运行func方法其值都是上次修改后的值,因为其在被初始化后,一直存在于静态存储区,下次调用func函数使用的是同一块内存区的数据。

a是一个自定义类型的静态局部变量,cb是一个自定义类型的静态局部常量,在VS2010中,在函数第一次被调用的时候按照定义的顺序依次初始化。

程序输出为:

Entered main()
1
An A instance is initializing...
An B instance is initializing...
2
3
An B instance is being destroyed...
An A instance is being destroyed...

 

静态方法:用static关键字修饰的方法(函数)

static void func()
{
    cout<<"This is a static function."<<endl;
}

上面代码所示的静态方法只在当前源文件中可以被调用。

 

Static的作用

1. 隐藏全局变量和方法。定义在一个源文件中的全局变量,在另一个源文件中通过前置extern关键字就可以声明该变量定义于其他源文件,然后直接使用。如果定义全局变量时前置static关键字,该变量就只在当前源文件中可见。用static关键字修饰的方法也只在当前源文件可见。这个特性可以让我们通过定义静态全局变量和静态方法来达到不同源文件中定义同名变量和同名方法的目的。

2. 保持变量持久化。使用static修饰的变量,存储在静态存储区,程序运行期间不会被回收。

3. 初始化变量为0。无论是静态全局变量还是静态局部变量,如果没有在定义时显式给予初始值,都会初始化为0.

全局变量和静态全局变量的异同:
1. 二者都存储在静态存储区,没有被显式赋予初始值时都会被初始化为0
2. 二者的作用域不同,定义于一个源文件中的全局变量在其他源文件中被extern关键字修饰后可以直接使用,静态全局变量在其他源文件中被隐藏。

局部变量和静态局部变量
局部变量存储在栈上,在定义它的方法或语句块结束后会被自动销毁,静态局部对象在定义它的方法或者语句块结束后仍然存在于静态存储区,下次再次调用该方法或语句块时保持上次修改后的值。

C++面向对象编程方式下的static关键字
在类中,静态成员指用static关键字修饰的成员,可以是成员方法或者数据成员。类的静态成员是属于类的,和类的实例无关,可以直接通过类名来调用,但是只能访问类的静态数据成员或者调用其他的静态方法成员,因为静态成员方法没有this指针。

#include<iostream>
#include<string>
using namespace std;
void PrintOutput(const string output)
{
    cout<<output<<endl;
}
class A
{
public:
    A(){PrintOutput("An A instance is initializing...");}
    ~A(){PrintOutput("An A instance is being destroyed...");}
};
class B
{
public:
    B():_iVar(9){PrintOutput("An B instance is initializing...");}
    ~B(){PrintOutput("An B instance is being destroyed...");}
    int _iVar;
};
class Sample
{
public:
    Sample(){++siCount;}
    ~Sample(){--siCount;}
    static int siCount;
    static A a;
    static const int sciVar;
    static const B cb;
    static void printSelf(){cout<<"I am class Sample. my instances are: "<<siCount<<endl;}
};
int Sample::siCount;
const int Sample::sciVar = 8;
A Sample::a;
const B Sample::cb;
int main()  
{
    cout<<"Entered main()"<<endl;
    cout<<Sample::siCount<<endl;
    cout<<Sample::sciVar<<endl;
    cout<<Sample::cb._iVar<<endl;
    Sample::printSelf();
    Sample *s1 = new Sample();
    Sample *s2 = new Sample();
    Sample::printSelf();
    delete s1;
    Sample::printSelf();
    delete s2;
    return 0;  
}

上面代码中数据成员siCount,sciVar,a,cb和成员方法printSelf都是静态的,可以通过Sample直接调用,不需要实例化Sample。

程序输出为:

An A instance is initializing...
An B instance is initializing...
Entered main()
0
8
9
I am class Sample. my instances are: 0
I am class Sample. my instances are: 2
I am class Sample. my instances are: 1
An B instance is being destroyed...
An A instance is being destroyed...

可以看到类的静态数据成员在程序进入main函数之前就初始化了,siCount表示该类所拥有的实例个数。

这里使用静态常量数据成员就是为了保证该常量成员只和类本身有关系,和类的实例没有关系,如果定义为非静态常量数据成员,那么类的每个实例都会有一个常量的拷贝。