19_static 关键字

19 static 关键字

用 static 修饰的就是一个全局变量, 它是一个私有的全局变量

面向过程设计中的 static

我们先看一下它在面向过程的时候应该如何去使用

"私有" 的全局变量

void Function(int nFlag)
{
    static char szBuffer[0x10];

    if(nFlag)
    {
        strcpy(szBuffer, "编程达人");
    }
    else
    {
        printf("%s \n", szBuffer);
    }
}

全局变量的特点就是它是分配在全局区, 程序启动的时候这个内存就有了, 一个地方改了这个全局变量, 其它地方都会有体现

如果我的全局变量只给一个函数用, 能不能做到 ?

这个时候其实就是 static

上面那个例子就是只给专门一个函数用的全局变量 szBuffer 数组

一般而言, 函数内部的局部变量会随着函数结束, 在栈中被释放掉, 但是被 static 修饰后, 它变成了全局变量, 不会被释放掉

static 也可以修饰函数, 然后这个函数只能在当前这个文件有效, 其它地方就用不了了

同样, 在变量前面加上 static, 这个变量就只能在当前文件中使用了

extern 修饰的函数或者变量, 就是告诉编译器这个东西在其它地方或者其它文件

extern void Fn(); 函数声明, 告诉编译器这个函数在其它文件

这就是面向过程的 static, 纯 C 语言代码的 static

面向对象设计中的 static 之静态数据成员

你只要记住, static 修饰的这个东西就是一个全局变量, 只不过它是私有的而已

class CBase
{
    private:
        int x;
        int y;
        static int z;   // 私有的全局变量, 只给 CBase 使用
};

// 静态成员的初始化
int CBase::z = 0;   // 你可能会问, 这个不是私有的吗, 私有的为什么你可以在外边使用 ?
// 这里注意, 这里面不是直接访问 z, 而是初始化, 所以语法上是可以的

int main(int argc, char* argv[])
{
    CBase c;    // 对象从栈中创建
    printf("%d \n",sizeof(CBase));  // 输出当前类型的大小, 结果是 8 而不是 12
    // 原因很简单, z 根本不是和 CBase 一体的, 它就是一个全局变量, 只不过这个全局变量只有 CBase 能访问而已
    // 无论你当前的 CBase 是在栈中创建还是堆中创建, 和 z 没关系, z 并不是 CBase 对象的成员

    return 0;
}
  1. 静态数据成员存储在全局数据区, 并且必须初始化, 静态数据成员初始化的格式为

    <数据类型> <类名> :: <静态数据成员名> = <值>;

  2. 静态数据成员和普通数据成员一样遵从 public, protected, private 访问规则

  3. 类的静态数据成员有两种访问形式

    <类对象名>.<静态数据成员名>
    <类类型名>::<静态数据成员名>

  4. 同全局变量相比, 使用静数据成员有两个优势

    • 避免命名冲突

    • 可以实现信息隐藏

class CBase
{
    private:
        int x;
        int y;
        static int z;
    public:
        void SetValue(int x)
        {
            z = x;
        }
        int GetValue()
        {
            return z;
        }
};

int main(int argc, char* argv[])
{
    CBase c, c1;

    c.SetValue(10);

    int x = c1.GetValue();

    printf("%d \n", x);

    return 0;
}

我用 c 来给 z 赋值, 用 c1 来获取这个 z, 最后输出结果是 10

这个 z 不再属于这个对象了, 这个 z 变成了全局变量, CBase 所有成员都可以s使用这个 z

面向对象设计中的 static 之成员函数

class CBase
{
    public:
        CBase(int x, int y);
        static int GetSum();    // 声明静态成员函数
    private:
        int x, y;
        static int Sum; // 声明静态数据成员
};

int CBase::Sum = 0; // 定义并初始化静态数据成员

CBase::CBase(int x, int y)
{
    this->x = x;
    this->y = y;
}

int CBase::GetSum() // 只要在上面声明的时候加上 static 就够了, 这里实现的时候不要加 static
{
    return Sum;
}

int main(int argc, char* argv[])
{

}

为什么我们要用 static 修饰函数 ?

通常情况下, 我们想访问一个对象里面的函数, 我们必须先创建一个对象, 然后通过 . 调用对象里面的成员

但是如果一个函数前面加上了 static, 这就说明这个函数不再属于任何一个对象了, 它是一个全局的, 只不过它只属于 CBase 的一个全局函数, 我们访问它的时候不用创建对象, 直接 CBase::GetSum(); 就可以访问, 这就是静态成员函数存在的唯一意义

当然了, 我创建一个对象, 然后用 . 去访问这个函数

它存在的价值就是在没有对象的情况下直接通过类名访问

总结 :

  1. 出现在类体外的函数定义不能指定关键字 static

  2. 静态成员之间可以相互访问, 包括静态成员函数访问静态数据成员和访问静态成员函数

    我可以再 GetSum 函数里面直接访问 z, 但是不能访问 x, 很好理解, GetSum() 和 z 是全局的, 但是 x y 不是, x y 是属于对象的, 换句话说当我没有创建对象之前就没有 x y, 但不管你有无创建对象, 这个 Sum 就已经存在了

  3. 非静态成员函数可以任意地访问静态成员函数和静态数据成员

    类里面的任何一个函数, 都可以直接访问这个静态成员, 但是要注意它是全局的, 一改动它就会影响到当前类的所有对象

  4. 静态成员函数不能访问非静态成员函数和非静态数据成员

  5. 调用类的静态成员函数的两种方式

    • <类名> :: <静态成员函数名> (<参数列表>)

    • <对象名>.<静态成员函数名> (<参数列表>)

    • <指针> -> <静态成员函数名> (<参数列表>)

static 关键词的经典应用 : 单子模式

有些时候我们希望定义的某些类只能有一个对象存在 (因为一个对象就足够了), 该如何进行限制呢 ?

实现思路 :

  1. 禁止对象随便被创建

  2. 保证对象只有一份存在

实现上面的这种方式我们叫作单子模式

我们平时创建对象的时候是用构造函数创建的, 只要有构造函数就意味着我想创建几个对象就创建几个对象, 换句话说, 如果你让使用这个类的人直接访问构造函数的话, 那么你就无法保证一个类只有一个对象存在

所以我们第一件事情就是我们自己写一个构造函数, 并且还要把我们的构造函数私有化, 私有了之后就无法创建对象了, 第一步不让别人随意创建对象的目的达到了

现在问题是任何人都没有办法创建对象了

class CSingleton
{
    public:
        CSingleton* GetInstance()
        {
            return new CSingleton();
        }
    private:
        CSingleton(){}
};

不过类成员内部的函数是可以访问私有函数的, 于是我就写一个函数, 返回值是当前对象的指针

但是现在就有了一个新的问题, 我要使用这个 GetInstance 函数就必须要先创建一个对象, 但是我现在无法创建对象

怎样才能在没有对象的情况下也能使用这个函数呢 ? 那就把这个函数声明成静态的

class CSingleton
{
    public:
        static CSingleton* GetInstance()    // 声明成静态函数
        {
            return new CSingleton();
        }
    private:
        CSingleton(){}
};

这样我们可以通过类名直接访问这个函数就行了

现在问题是, 如果你每次调用这个函数, 它都是返回一个新的对象的话, 那不就仍然没有达到目的了吗 ?

好办, 我可以再写一个静态的指针

class CSingleton
{
    public:
        static CSingleton* GetInstance()
        {
            return new CSingleton();
        }
    private:
        CSingleton(){}
        static CSingleton* m_pInstance; // 静态成员, 静态成员就是全局变量, 只有当前这个类可以访问
};

如果这个指针成员为空的话, 我就给它赋值并返回

class CSingleton
{
    public:
        static CSingleton* GetInstance()
        {
            if(m_pInstance == NULL)
                m_pInstance = new CSingleton();
            return m_pInstance;
        }
    private:
        CSingleton(){}
        static CSingleton* m_pInstance; // 定义静态成员
};

现在既然是静态成员, 那我们首先就要对它进行初始化

初始化静态成员 :

CSingleton* CSingleton::m_pInstance = NULL;

我们可以做实验验证, 连续创建两个对象, 返回的地址是一模一样的, 说明我们的单子模式是成功的