static
前面的课程我们介绍了:普通的数据成员和普通的成员函数的定义方法,又介绍了const关键字,并且讲解了用const关键字来修饰数据成员和成员函数,我们把这样的数据成员和成员函数称之为常数据成员和常成员函数。
这节课,我们再为大家介绍一个关键字:static(静态的)。在C++中提到静态,就不得不提到两个基本概念:静态数据成员和静态成员函数。我们以一个例子来看一下静态数据成员和静态成员函数的定义方法。
这里,我们定义了一个Tank类(坦克),在这个Tank类中,我们定义了一个普通的数据成员(string m_strCode;),那么如何去定义一个静态数据成员呢?那就是,就在数据成员的前面加上关键字static即可(如:static int s_iCount;),此时我们就称这个数据成员m_iCount为静态数据成员。又如何去定义一个静态成员函数呢?我们就在成员函数的前面加上关键字static即可(如:static int getCount() { return s_iCount; })。
问题:那么什么情况下我们要用到静态数据成员和静态成员函数呢?我们来描述一个场景。
大家都玩过坦克大战的游戏吧,你会发现当自己方的坦克非常多的时候,每辆坦克作战就会很英勇;当自己方坦克比敌方少很多的时候,就会变得很胆怯。那么,这样的话,你就希望每辆坦克作为对象来说,都能够知道自己方还有多少辆坦克的存在,那么此时我们就需要一个静态变量来记录这个值,这个值在我们的这个例子中就是m_iCount。
作为静态变量来说,它并不依赖于对象,而是依赖于类。这如何来理解呢?如果我们不实例化对象,那么作为静态的数据成员s_iCount仍然在内存中是存在的,这个也是静态数据成员与普通数据成员最大的区别。如果是普通的数据成员,则必须要实例化之后,这个数据成员在内存中才能够存在。
那么对于静态数据成员来说,因为它并不依赖于对象的实例化,所以静态的数据成员并不在构造函数中去实例化,它的实例化过程往往是单独进行的。如(int Tank::m_iCount = 0;)请大家注意:为静态数据成员初始化的时候,不要再加static关键字了,而直接写成:类型+类名+数据成员的名称+初值。此外,我们对于这个类来说,大家也可以看一看所书写的算法:我们定义了一个s_iCount(表示坦克的数量),刚初始化的时候坦克数量为0,如果我们将“s_iCount++”写在构造函数中,将“s_iCount--”写在析构函数中,那么,每当我们实例化一个坦克对象的时候,坦克数量就会增加一个;每当销毁一辆坦克的时候,坦克数量就会减少一个;而作为每个对象来说,都可以通过直接访问s_iCount来获取到自己同伴的数量。那么,访问的时候,作为访问方法来说又有两种:一种是不通过对象,而是直接通过类的访问方法;两一种是,如果实例化了一个对象,也可以通过这个对象来访问的方法。如下所示:
下面我们再来从内存当中给大家强调一下普通数据成员和静态数据成员究竟有什么区别。
我们还是以Tank这个类为例,当我们通过Tank这个类实例化t1、t2、t3和t4这4个对象之后,作为普通数据成员code就分别随着t1、t2、t3和t4的产生而诞生了,并且诞生了4个code,每个code都有自己的编号。可是,在这4个对象诞生之前s_iCount就已经诞生了,而且只诞生一次。即:t1、t2、t3和t4这4个对象产生的过程当中,s_iCount的值会变化,但是s_iCount这个静态数据成员的个数不会发生变化。
在前面的例子当中,我们是使用的普通的数据成员调用静态的数据成员或成员函数,那么,反之,用静态成员函数去调用普通的数据成员或者是普通的成员函数是不是一样成立呢??答案是:不可行的!!如果这样来写,编译器就会报错!!从逻辑上来讲,静态的数据成员和静态的成员函数都是随类的产生而产生,也就是说,其是依赖于类的;而普通的数据成员是依赖于对象的。如果,一个对象如果产生的话,那么我们在静态的成员函数当中去调用非静态的数据成员,显然是会失败的。因为人家一个对象还没有呢,这就是一个时机的问题。而从原理上来说,也是不成立的。
从this指针来谈静态成员函数
我们修改一下Tank这个类,如下: