设计模式:单例模式

单例模式 :创建型模式,创建只能产生一个对象的实例的类--用私有构造函数实现类的定义

(1)单例类的基本概念和实现

类的构造函数设置为私有,防止在栈或堆创建对象;

意思就是,static 整个类,并且只有这一个类,这个类在程序的结束才会消失

在new一个对象的时候,先调用malloc分配一块内存,然后调用构造函数分配初始化这块内存,最后通过指针指向这块内存;

单例类在多线程中可能导致的问题

情况:线程安全的情况,当两个线程由于第一个执行判断成立之后,由于cpu时间片这时第二个也执行并且分配了对象内存,而此时又调用回原来的线程,又分配了一次,一共两次,混乱了不是单例类。

解决方法:

一个好的方法就是在main主函数程序入口中,在创建任何其他线程之前先执行一次这个静态方法把类创建出来;

饿汉式:

很饥渴很迫切,程序一执行,不管掉没掉用这个静态方法,都直接提前创建对象分配好,这样不为空也就不用加锁;单例类被创建,不受多线程干扰;

懒汉式:

当你要用的时候在去调用这个静态方法创建对象,减少了内存分配,合理利用资源,但是要注意多线程,最好的解决方法就是在程序执行前先调用一次(但是这类似与饿汉式了)

内存释放问题

饿汉式在类中定义一个只用来调用析构函数的类,然后外部定义这个静态类,当程序结束后,这个静态类,就会直接释放单例类的对象的内存

懒汉式则是在判断是不是为空的时候,也就是第一次调用的时候就会创建这个类,同样程序结束后,就会释放;

static内存深饰

在C++中,编译后的目标文件(.o文件,或者在Windows系统下的.obj文件)确实是程序执行前的一种中间产物。在这个阶段,变量的内存分配已经确定,但它们的实际运行时地址和初始化行为还没有具体落实,这需要等待程序链接并加载到内存中。

编译和链接过程

为了更好地理解静态成员变量在编译和链接过程中的处理,以下是一个简要的过程描述:

  1. 编译(Compilation)
  • 源代码(.cpp文件)被编译成目标文件(.o或.obj文件)。

  • 目标文件包含了机器码和符号表,但还不是一个完整的可执行文件。

  • 静态成员变量在目标文件中分配了符号,但没有具体的地址。

  1. 链接(Linking)
  • 链接器将多个目标文件和库文件链接成一个可执行文件。

  • 链接器会将符号解析为具体的内存地址,并安排变量到相应的内存段(如BSS段和数据段)。

  • 未初始化的静态变量符号会被放置在BSS段的部分,已初始化的静态变量符号会被放置在数据段的部分。

  1. 加载(Loading)
  • 可执行文件在操作系统的加载器(loader)的控制下加载到内存中。

  • 加载器将BSS段清零,并将数据段的内容加载到相应的内存位置。

在.o文件中的情况

在目标文件(.o文件)中,静态成员变量的具体内存分配还没有完全确定,但符号已经分配到了相应的段(section)。

  • 未初始化的静态成员变量

  • 在目标文件中,这些变量被分配到 .bss 段。

  • .bss 段只记录了变量的符号和大小信息,不占用实际的文件空间,因为它在加载时被操作系统清零。

  • 已初始化的静态成员变量

  • 在目标文件中,这些变量被分配到 .data 段。

  • .data 段包含了变量的符号和初始值信息,占用实际的文件空间。

cpp 复制代码
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 段加载到内存并初始化相应的变量。

通过以上描述,你可以更清晰地了解静态成员变量在编译、链接和加载过程中的处理。

运行时;

代码完整:

cpp 复制代码
#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;
}
相关推荐
狂奔的sherry11 小时前
单例模式(巨通俗易懂)普通单例,懒汉单例的实现和区别,依赖注入......
开发语言·c++·单例模式
晨星05272 天前
软件设计模式之单例模式
单例模式·设计模式
code bean4 天前
【wpf】WPF开发避坑指南:单例模式中依赖注入导致XAML设计器崩溃的解决方案
单例模式·wpf
是三好5 天前
单例模式(Singleton Pattern)
java·开发语言·算法·单例模式
青春易逝丶5 天前
单例模式
单例模式
YA3335 天前
java设计模式一、单例模式
java·单例模式·设计模式
枫景Maple6 天前
Unity中多线程与高并发下的单例模式
unity·单例模式·游戏引擎
iiiiaaiashah6 天前
单例模式的mock类注入单元测试与友元类解决方案
java·开发语言·单例模式
jingfeng51410 天前
线程池及线程池单例模式
linux·开发语言·单例模式
Brookty10 天前
深入解析Java并发编程与单例模式
java·开发语言·学习·单例模式·java-ee