一、问题背景
现象就是我在翻自己以前写的代码时,看到了在做一个单例模式的时候,私有成员在外部做了初始化,心想私有成员怎么能在外部拿到呢?是不是忘记了什么概念?于是便有了下文。
#include <iostream>
class Game {
private:
Game(){} // 构造函数私有
Game(const Game &p){} // 复制构造函数也私有
static Game *game; // 静态Game类指针对象
public:
// 获取对象的接口函数,单例模式
static Game *getInstance(void);
//游戏运行的逻辑代码
void play(void);
};
Game *Game::game = new Game; // 静态成员,类外申请堆区空间
Game *Game::getInstance(void) {
return game;
}
void Game::play(void) {
// 实现play的逻辑代码
}
int main()
{
Game *p = Game::getInstance();
p->play();
return 0;
}
从上述代码可以看到,这里是做了一个单例模式的运行,确保只有一个Game类的对象再跑,就是限制只能开一个游戏的意思,不过这个例子里的单例方式在多线程的情况下是有问题的,这里不多说,不是本文要关注的问题。
可以看到这个Game*类型的指针成员是在私有成员下的,按照私有成员的概念:所有的成员属性和成员函数可以在类内访问,不能在外部访问。为什么在这里能够在外部做初始化呢?
二、了解原理,解释问题
静态成员是不属于任何对象的,他是属于类本身的。就是说现在例如有一个类叫做Game,类里面有个静态成员叫做D,创建三个对象A、B、C,它们三个的成员D其实是同一个!用代码解释:
#include <iostream>
class Game {
public:
static int D; // 静态成员变量
void printD() {
std::cout << "D = " << D << std::endl;
}
};
// 初始化静态成员变量
int Game::D = 0;
int main() {
Game A;
Game B;
Game C;
// 修改静态成员变量的值
Game::D = 10;
// 通过对象访问静态成员变量
A.printD(); // 输出 D = 10
B.printD(); // 输出 D = 10
C.printD(); // 输出 D = 10
return 0;
}
这里我就不贴运行结果了,结果就是他们三个打印出来的D呢是同一块内存空间上的值,也就是说,类的静态数据成员存储在类的静态存储区,这个区域是全局的,与类的任何特定对象实例都无关。简单的说就是静态成员有自己的静态区,它被存放在了那,而且多个对象的静态成员都是这块物理地址。因此,无论创建了多少个类的对象,它们都共享同一个静态数据成员。
那么我们就可以解释为什么能在外部摸到这个私有的静态成员了,因为初始化是在类的静态存储区进行的,并且是在程序的静态初始化阶段完成的而不是通过对象去进行访问的。这种初始化是不受私有访问限制的影响,因为它是类作用域的一部分。
于是编译器会允许这种在类外部对私有静态成员进行初始化。
浅浅的把自己的解惑过程贴出来,做一个记录,搞不好哪一天我又忘了,乐)