static细谈

C++的static有两种用法:面向过程程序设计中的static和面向对象程序设计中的static。前者应用于普通变量和函数;后者主要应用于类。
1、面向过程设计
1.1、静态全局变量
首先看一下什么是全局变量,正如下面的代码,定义int n 为全局变量,此时该文件中的任意位置都能使用该变量。我们发现全局变量在声明的时候没有初始化,系统会自动初始化为0。

#include <iostream>
using namespace std;
void func();
int n;//全局变量
int main()
{
    n=10;
    cout<<"main "<<n<<endl;
    func();
    return 0;
}

void func()
{
    n++;
    cout<<"func "<<n<<endl;
}

输出结果:

main 10
func 11

但什么是静态全局变量呢,我们看下面的代码。我们发现,正是在全局变量前面加static便形成静态全局变量。

#include <iostream>
using namespace std;
void func();
static int n;//静态全局变量
int main()
{
    n=10;
    cout<<n<<endl;
    func();
    return 0;
}

void func()
{
    n++;
    cout<<n<<endl;
}

输出结果:

main 10
func 11

我们发现输出结果一样,那普通的全局变量和静态全局变量到底有什么区别呢。现在讲的程序都是位于一个文件中,当程序位于两个文件中时,便会发现问题,我们实验一下。
当声明为普通全局变量时:

//file1
#include <iostream>
#include "func.h"
using namespace std;
int n;//普通全局变量
int main()
{
    n=10;
    cout<<"main "<<n<<endl;
    func();
    return 0;
}
//file2
#include <iostream>
using namespace std;
extern int n;
void func()
{
    n++;
    cout<<"func "<<n<<endl;
}

输出结果:

main 10
func 11

当声明为静态全局变量时:

//file1
#include <iostream>
#include "func.h"
using namespace std;
static int n;//静态全局变量
int main()
{
    n=10;
    cout<<"main "<<n<<endl;
    func();
    return 0;
}
//file2
#include <iostream>
using namespace std;
extern int n;
void func()
{
    n++;
    cout<<"func "<<n<<endl;
}

此时编译出现问题如下

test.cpp:4:12: error: 'n' was declared 'extern' and later 'static' [-fpermissive]
 static int n;//静态全局变量
            ^
In file included from test.cpp:2:0:
func.h:3:12: note: previous declaration of 'n'
 extern int n;

表示一个文件中定义了静态全局变量,在其他文件中访问该变量会出现问题。所以静态全局变量和普通全局变量的区别在于多文件中同一变量的传递,静态全局变量只在其所在的文件起作用。此时,我们可以想到,静态变量可以保证其他文件可以使用相同名字的变量,因为互相不影响。
1.2、静态局部变量
我们将n定义为静态局部变量,代码如下:

#include <iostream>
using namespace std;
void func();
int main()
{
    func();
    func();
    func();
    return 0;
}

void func()
{
    static int n=10;//静态局部变量
    cout<<"func "<<++n<<endl;
}

编译输出结果:

func 11
func 12
func 13

哎,我们不禁纳闷了,为什么调用三次func函数,每次都要重新定义并初始化n为10,为啥结果不一样呢。这就是static的妙处了,通常,在函数体内定义了一个变量,每当程序运行到该语句时都会给该局部变量分配栈内存。但随着程序退出函数体,系统就会收回栈内存,局部变量也相应失效。但有时候我们需要在两次调用之间对变量的值进行保存。通常的想法是定义一个全局变量来实现。但这样一来,变量已经不再属于函数本身了,不再仅受函数的控制,给程序的维护带来不便。
静态局部变量正好可以解决这个问题。静态局部变量保存在全局数据区,而不是保存在栈中,每次的值保持到下一次调用,直到下次赋新值。
静态局部变量有以下特点:
• 该变量在全局数据区分配内存;
• 静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化;
• 静态局部变量一般在声明处初始化,如果没有显式初始化,会被程序自动初始化为0;
• 它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,当定义它的函数或语句块结束时,其作用域随之结束。
1.3、静态函数
在函数的返回类型前加上static关键字,函数即被定义为静态函数。静态函数与普通函数不同,它只能在声明它的文件当中可见,不能被其它文件使用,在其他文件中也可以定义相同名字的函数,不会互相影响。

#include <iostream>
using namespace std;
static void func();
int main()
{
    func();
    func();
    func();
    return 0;
}

void func()
{
    int n=10;
    cout<<"func "<<++n<<endl;
}

2、面向对象设计
2.1静态数据成员
我们小时候都玩过坦克大战的游戏,我方每个坦克都得知道自己方有多少坦克,这才能去给你打地方坦克。当我们定义了一个Tank类的时候,我们应该能清晰的知道,现在我们已经有多少Tank这样的对象了,此时我们定义一个静态数据成员,用于保存这个量。我们定义Tank类如下

class Tank
{
public:
    Tank(string strCode)
    {
        s_iCount++;
        m_strCode=strCode;
    }
    ~Tank()
    {
        s_iCount--;
    }
    int getCount()
    {
        return s_iCount;
    }
    static int s_iCount;//静态数据成员
private:
    string m_strCode;//Tank代号
};
int Tank::s_iCount=0;//单独初始化

我们看到静态数据成员不是位于构造函数中初始化,而是单独在外面初始化。实例化几个对象我们看一下,有什么不一样。

int main()
{
    Tank tank1("01");
    cout<<tank1.getCount()<<endl;//通过对象访问,普通成员函数调用静态数据成员
    Tank tank2("02");
    cout<<Tank::s_iCount<<endl;//通过类访问
    return 0;
}

编译运行结果:

1
2

观察上述验证我们可以看出静态数据成员有以下特点:
(1)对于非静态数据成员,每个类对象都有自己的拷贝。而静态数据成员被当作是类的成员。无论这个类的对象被定义了多少个,静态数据成员在程序中也只有一份拷贝,由该类型的所有对象共享访问。也就是说,静态数据成员是该类的所有对象所共有的。对该类的多个对象来说,静态数据成员只分配一次内存,供所有对象共用。所以,静态数据成员的值对每个对象都是一样的,它的值可以更新;
(2)静态数据成员存储在全局数据区。静态数据成员定义时要分配空间,所以不能在类声明中定义;
(3)因为静态数据成员在全局数据区分配内存,属于本类的所有对象共享,所以,它不属于特定的类对象,在没有产生类对象时其作用域就可见,即在没有产生类的实例时,我们就可以操作它;
(4)静态数据成员和普通数据成员一样遵从public,protected,private访问规则;
(5)普通的成员函数可以访问静态数据成员。例子中的tank对象通过getCount成员函数访问静态数据成员s_iCount。

2.2静态成员函数
我们对上述Tank类中的getCount成员函数进行修改。

class Tank
{
public:
    Tank(string strCode)
    {
        s_iCount++;
        m_strCode=strCode;
    }
    ~Tank()
    {
        s_iCount--;
    }
    static int getCount()
    {
        return s_iCount;
    }
    static int s_iCount;//静态数据成员
private:
    string m_strCode;//Tank代号
};
int Tank::s_iCount=0;//单独初始化

此时和常规的又有什么区别呢

int main()
{
    Tank tank1("01");
    cout<<tank1.getCount()<<endl;
    Tank tank2("02");
    cout<<Tank::s_iCount<<endl;
    cout<<Tank::getCount()<<endl;
    return 0;
}

编译输出结果

1
2
2

这是什么意思呢,未实例化对象,通过类也能访问成员函数,但必须是静态成员函数。
上述我们看到了普通的成员函数可以调用静态成员函数,那静态成员函数调用普通的数据成员会怎样,我们看下面的代码

class Tank
{
public:
    Tank(string strCode)
    {
        s_iCount++;
        m_strCode=strCode;
    }
    ~Tank()
    {
        s_iCount--;
    }
    static int getCount()
    {
        m_strCode="01";//调用普通数据成员
        return s_iCount;
    }
    static int s_iCount;//静态数据成员
private:
    string m_strCode;//Tank代号
};
int Tank::s_iCount=0;//单独初始化

编译出现下面的问题

test.cpp: In static member function 'static int Tank::getCount()':
test.cpp:18:9: error: invalid use of member 'Tank::m_strCode' in static member function
         m_strCode="01";
         ^~~~~~~~~
test.cpp:23:12: note: declared here
     string m_strCode;//Tank代号

说明静态成员函数不能调用普通数据成员,为什么呢?我们记住下面的两点。
静态成员(数据、对象)是类定义的时候产生,普通成员(数据、对象)是实例化对象的时候产生。所以用到当普通成员调用静态成员的时候肯定已经有了类的定义,所以此时是可行的;如果当静态成员调用普通成员,此时对象不一定存在,所以编译器肯定不能通过。
关键一点:对类求大小的时候不包括静态数据成员!!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值