• 设计模式:单例模式


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

    (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 段包含了变量的符号和初始值信息,占用实际的文件空间。

    1. class Example {
    2. public:
    3. static int uninitializedVar;
    4. static int initializedVar;
    5. };
    6. int Example::uninitializedVar; // 定义但未初始化
    7. int Example::initializedVar = 100; // 定义并初始化

    在编译成目标文件后:

    • uninitializedVar 会出现在 .bss 段。

    • initializedVar 会出现在 .data 段,并包含其初始值 100。

    总结

    • .o文件 中包含了代码和数据的符号信息,以及未初始化变量的符号和大小(在 .bss 段中),已初始化变量的符号和初始值(在 .data 段中)。

    • 程序执行前:.o文件中的符号还没有具体的内存地址,但已经安排到相应的段中等待链接和加载。

    • 加载时:操作系统会将 .bss 段清零,将 .data 段加载到内存并初始化相应的变量。

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

    运行时;

    代码完整:

    1. #include
    2. #include
    3. using namespace std;
    4. // 饿汉式单例模式
    5. namespace name1 {
    6. class Gameconfig {
    7. private:
    8. Gameconfig() {
    9. cout << "饿汉模式构造函数" << endl;
    10. }
    11. ~Gameconfig() {
    12. cout << "饿汉模式析构函数" << endl;
    13. }
    14. // 禁用拷贝构造函数和赋值运算符
    15. Gameconfig(const Gameconfig&) = delete;
    16. Gameconfig& operator=(const Gameconfig&) = delete;
    17. // 静态实例指针,在类加载时初始化
    18. static Gameconfig* instance;
    19. static Gameconfig* initializeInstance() {
    20. cout<<"饿汉模式被提前创建对象"<
    21. return new Gameconfig();
    22. }
    23. // 用于自动释放单例对象的嵌套类
    24. class A {
    25. public:
    26. ~A() {
    27. if (instance != nullptr) {
    28. delete instance;
    29. instance = nullptr;
    30. cout << "饿汉模式被A析构销毁" << endl;
    31. }
    32. }
    33. };
    34. static A a; // 静态嵌套类实例
    35. public:
    36. int test=12222;
    37. static Gameconfig* getInstance() {
    38. return instance;
    39. }
    40. void show() {
    41. cout << "饿汉模式" << endl;
    42. }
    43. void settest()
    44. {
    45. Gameconfig::test=1;
    46. }
    47. };
    48. // 初始化静态成员
    49. Gameconfig* Gameconfig::instance = Gameconfig::initializeInstance();//在程序运行前就已经初始化,这样避免线程;
    50. Gameconfig::A Gameconfig::a; // 静态嵌套类实例
    51. }
    52. //懒汉模式单例
    53. namespace name2{
    54. class Singleton
    55. {
    56. private:
    57. static Singleton*n_instance;//定义指向本身的指针和锁
    58. static pthread_mutex_t mutex;
    59. private:
    60. Singleton(){
    61. cout<<"懒汉模式构造函数"<
    62. }
    63. ~Singleton(){
    64. cout<<"懒汉模式析构函数"<
    65. }
    66. //禁用拷贝构造和赋值构造函数
    67. Singleton(const Singleton&) = delete;
    68. Singleton& operator=(const Singleton&) = delete;
    69. public:
    70. static Singleton*getinstance(){
    71. if(n_instance==nullptr){
    72. //上锁
    73. pthread_mutex_lock(&mutex);
    74. if(n_instance==nullptr){
    75. n_instance=new Singleton();
    76. cout<<"懒汉分配对象完毕"<
    77. }
    78. //解锁
    79. pthread_mutex_unlock(&mutex);
    80. }
    81. return n_instance;
    82. }
    83. void show(){
    84. cout<<"懒汉模式"<
    85. }
    86. class A{
    87. public:
    88. ~A(){
    89. if(n_instance!=nullptr){
    90. delete n_instance;
    91. n_instance=nullptr;
    92. cout<<"懒汉被A销毁"<
    93. }
    94. }
    95. };
    96. static A a;
    97. };
    98. Singleton*Singleton::n_instance=nullptr;
    99. pthread_mutex_t Singleton::mutex = PTHREAD_MUTEX_INITIALIZER;
    100. Singleton::A Singleton::a;
    101. }
    102. int main() {
    103. /*name1::Gameconfig* sig = name1::Gameconfig::getInstance();
    104. sig->show();
    105. cout<test<
    106. sig->settest();
    107. name1::Gameconfig* sik = name1::Gameconfig::getInstance();
    108. cout<test<
    109. name2::Singleton*sig=name2::Singleton::getinstance();
    110. sig->show();
    111. return 0;
    112. }

  • 相关阅读:
    npm安装包卡住问题
    Matplotlib中的“plt”和“ax”,设置大小刻度,设置实线和虚线方格线
    postman接口测试中文汉化教程
    八、PL/SQL 记录
    屎山代码踩坑记录:不要将一个类写的臃肿
    dva.js打包 hash文件名
    MySQL的数据类型详解
    积分球荧光光谱测试光电检测方式有哪些优点?
    springboot 使用shiro集成阿里云短信验证码
    堆排序--C语言版
  • 原文地址:https://blog.csdn.net/wuxiaoyu0806/article/details/139578439