目录 Table of Contents
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;
}
-
静态数据成员存储在全局数据区, 并且必须初始化, 静态数据成员初始化的格式为
<数据类型> <类名> :: <静态数据成员名> = <值>;
-
静态数据成员和普通数据成员一样遵从 public, protected, private 访问规则
-
类的静态数据成员有两种访问形式
<类对象名>.<静态数据成员名>
<类类型名>::<静态数据成员名>
-
同全局变量相比, 使用静数据成员有两个优势
-
避免命名冲突
-
可以实现信息隐藏
-
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();
就可以访问, 这就是静态成员函数存在的唯一意义
当然了, 我创建一个对象, 然后用 .
去访问这个函数
它存在的价值就是在没有对象的情况下直接通过类名访问
总结 :
-
出现在类体外的函数定义不能指定关键字 static
-
静态成员之间可以相互访问, 包括静态成员函数访问静态数据成员和访问静态成员函数
我可以再 GetSum 函数里面直接访问 z, 但是不能访问 x, 很好理解, GetSum() 和 z 是全局的, 但是 x y 不是, x y 是属于对象的, 换句话说当我没有创建对象之前就没有 x y, 但不管你有无创建对象, 这个 Sum 就已经存在了
-
非静态成员函数可以任意地访问静态成员函数和静态数据成员
类里面的任何一个函数, 都可以直接访问这个静态成员, 但是要注意它是全局的, 一改动它就会影响到当前类的所有对象
-
静态成员函数不能访问非静态成员函数和非静态数据成员
-
调用类的静态成员函数的两种方式
-
<类名> :: <静态成员函数名> (<参数列表>)
-
<对象名>.<静态成员函数名> (<参数列表>)
-
<指针> -> <静态成员函数名> (<参数列表>)
-
static 关键词的经典应用 : 单子模式
有些时候我们希望定义的某些类只能有一个对象存在 (因为一个对象就足够了), 该如何进行限制呢 ?
实现思路 :
-
禁止对象随便被创建
-
保证对象只有一份存在
实现上面的这种方式我们叫作单子模式
我们平时创建对象的时候是用构造函数创建的, 只要有构造函数就意味着我想创建几个对象就创建几个对象, 换句话说, 如果你让使用这个类的人直接访问构造函数的话, 那么你就无法保证一个类只有一个对象存在
所以我们第一件事情就是我们自己写一个构造函数, 并且还要把我们的构造函数私有化, 私有了之后就无法创建对象了, 第一步不让别人随意创建对象的目的达到了
现在问题是任何人都没有办法创建对象了
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;
我们可以做实验验证, 连续创建两个对象, 返回的地址是一模一样的, 说明我们的单子模式是成功的