单例模式 :创建型模式,创建只能产生一个对象的实例的类--用私有构造函数实现类的定义
(1)单例类的基本概念和实现
类的构造函数设置为私有,防止在栈或堆创建对象;
意思就是,static 整个类,并且只有这一个类,这个类在程序的结束才会消失
在new一个对象的时候,先调用malloc分配一块内存,然后调用构造函数分配初始化这块内存,最后通过指针指向这块内存;
单例类在多线程中可能导致的问题
情况:线程安全的情况,当两个线程由于第一个执行判断成立之后,由于cpu时间片这时第二个也执行并且分配了对象内存,而此时又调用回原来的线程,又分配了一次,一共两次,混乱了不是单例类。
解决方法:
一个好的方法就是在main主函数程序入口中,在创建任何其他线程之前先执行一次这个静态方法把类创建出来;
饿汉式:
很饥渴很迫切,程序一执行,不管掉没掉用这个静态方法,都直接提前创建对象分配好,这样不为空也就不用加锁;单例类被创建,不受多线程干扰;
懒汉式:
当你要用的时候在去调用这个静态方法创建对象,减少了内存分配,合理利用资源,但是要注意多线程,最好的解决方法就是在程序执行前先调用一次(但是这类似与饿汉式了)
内存释放问题
饿汉式在类中定义一个只用来调用析构函数的类,然后外部定义这个静态类,当程序结束后,这个静态类,就会直接释放单例类的对象的内存
懒汉式则是在判断是不是为空的时候,也就是第一次调用的时候就会创建这个类,同样程序结束后,就会释放;
static内存深饰
在C++中,编译后的目标文件(.o文件,或者在Windows系统下的.obj文件)确实是程序执行前的一种中间产物。在这个阶段,变量的内存分配已经确定,但它们的实际运行时地址和初始化行为还没有具体落实,这需要等待程序链接并加载到内存中。
编译和链接过程
为了更好地理解静态成员变量在编译和链接过程中的处理,以下是一个简要的过程描述:
- 编译(Compilation):
-
源代码(.cpp文件)被编译成目标文件(.o或.obj文件)。
-
目标文件包含了机器码和符号表,但还不是一个完整的可执行文件。
-
静态成员变量在目标文件中分配了符号,但没有具体的地址。
- 链接(Linking):
-
链接器将多个目标文件和库文件链接成一个可执行文件。
-
链接器会将符号解析为具体的内存地址,并安排变量到相应的内存段(如BSS段和数据段)。
-
未初始化的静态变量符号会被放置在BSS段的部分,已初始化的静态变量符号会被放置在数据段的部分。
- 加载(Loading):
-
可执行文件在操作系统的加载器(loader)的控制下加载到内存中。
-
加载器将BSS段清零,并将数据段的内容加载到相应的内存位置。
在.o文件中的情况
在目标文件(.o文件)中,静态成员变量的具体内存分配还没有完全确定,但符号已经分配到了相应的段(section)。
-
未初始化的静态成员变量:
-
在目标文件中,这些变量被分配到
.bss
段。 -
.bss
段只记录了变量的符号和大小信息,不占用实际的文件空间,因为它在加载时被操作系统清零。 -
已初始化的静态成员变量:
-
在目标文件中,这些变量被分配到
.data
段。 -
.data
段包含了变量的符号和初始值信息,占用实际的文件空间。
class Example {
public:static int uninitializedVar;static int initializedVar;
};int Example::uninitializedVar; // 定义但未初始化
int Example::initializedVar = 100; // 定义并初始化
在编译成目标文件后:
-
uninitializedVar
会出现在.bss
段。 -
initializedVar
会出现在.data
段,并包含其初始值 100。
总结
-
.o文件 中包含了代码和数据的符号信息,以及未初始化变量的符号和大小(在
.bss
段中),已初始化变量的符号和初始值(在.data
段中)。 -
程序执行前:.o文件中的符号还没有具体的内存地址,但已经安排到相应的段中等待链接和加载。
-
加载时:操作系统会将
.bss
段清零,将.data
段加载到内存并初始化相应的变量。
通过以上描述,你可以更清晰地了解静态成员变量在编译、链接和加载过程中的处理。
运行时;
代码完整:
#include <iostream>
#include<pthread.h>
using namespace std;// 饿汉式单例模式
namespace name1 {class Gameconfig {private:Gameconfig() {cout << "饿汉模式构造函数" << endl;}~Gameconfig() {cout << "饿汉模式析构函数" << endl;}// 禁用拷贝构造函数和赋值运算符Gameconfig(const Gameconfig&) = delete;Gameconfig& operator=(const Gameconfig&) = delete;// 静态实例指针,在类加载时初始化static Gameconfig* instance;static Gameconfig* initializeInstance() {cout<<"饿汉模式被提前创建对象"<<endl;return new Gameconfig();}// 用于自动释放单例对象的嵌套类class A {public:~A() {if (instance != nullptr) {delete instance;instance = nullptr;cout << "饿汉模式被A析构销毁" << endl;}}};static A a; // 静态嵌套类实例public:int test=12222;static Gameconfig* getInstance() {return instance;}void show() {cout << "饿汉模式" << endl;}void settest(){Gameconfig::test=1;}};// 初始化静态成员Gameconfig* Gameconfig::instance = Gameconfig::initializeInstance();//在程序运行前就已经初始化,这样避免线程;Gameconfig::A Gameconfig::a; // 静态嵌套类实例
}//懒汉模式单例
namespace name2{class Singleton{private:static Singleton*n_instance;//定义指向本身的指针和锁static pthread_mutex_t mutex;private:Singleton(){cout<<"懒汉模式构造函数"<<endl;}~Singleton(){cout<<"懒汉模式析构函数"<<endl;}//禁用拷贝构造和赋值构造函数Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;public:static Singleton*getinstance(){if(n_instance==nullptr){//上锁pthread_mutex_lock(&mutex);if(n_instance==nullptr){n_instance=new Singleton();cout<<"懒汉分配对象完毕"<<endl;}//解锁pthread_mutex_unlock(&mutex);}return n_instance;}void show(){cout<<"懒汉模式"<<endl;}class A{public:~A(){if(n_instance!=nullptr){delete n_instance;n_instance=nullptr;cout<<"懒汉被A销毁"<<endl;}}};static A a;};Singleton*Singleton::n_instance=nullptr;pthread_mutex_t Singleton::mutex = PTHREAD_MUTEX_INITIALIZER;Singleton::A Singleton::a;}
int main() {/*name1::Gameconfig* sig = name1::Gameconfig::getInstance();sig->show();cout<<sig->test<<endl;sig->settest();name1::Gameconfig* sik = name1::Gameconfig::getInstance();cout<<sik->test<<endl;*/name2::Singleton*sig=name2::Singleton::getinstance();sig->show();return 0;
}