• 动态库中单例一记









    1       一个简单的Singleton

    我们先来看一个简单的单件类的定义:

    #include<iostream>

    class Singleton

    {

    public:

    static Singleton& GetSingleton()

    {

        static Singleton singleton;

        return singleton;

    }

    void Print()

    {

        std::cout<<"Singleton Print\n";

    }

    private:

    Singleton::Singleton()

    {

        std::cout<<"singleton constructor\n";

    }

    };

    这个类提供了一些的功能(函数Print), 并禁止我们创建这个类的对象(构造函数为private), 提供给我们一个静态函数接口来访问这个单件对象(GetSingleton),利用静态变量的特点实现了其单一性。但是, 这个类有问题吗?

    2       问题所在

    是的,上面这个简单的类的确存在问题,而且是一个很严重的问题,这个问题让Singleton类完全失去它存在的意义, 因为它不再唯一!

    是的, 当我们只在一个模块中使用这个类时(比如说,一个exe,这个类是没有问题的。但是, 一个稍微复杂一点的软件, 为了开发的便捷,提高复用度,降低耦合性等原因,其难免会被分成好几个模块。那么假设讲我现在有两个模块,一个DLL(singleton.dll), 用来提供一些基础的功能, 一个EXE(test.exe),用来提供真正的软件逻辑。 我现在singleton.dll中封装了一个Print的函数间(用类Singleton实现)并暴露出来。

    singleton.dll

    void Print()

    {

        Singleton::GetSingleton().Print();

    }

    并在test.exe中这样调用:

    Test.exe

    Singleton::GetSingleton().Print();

    Print();

    这个时候,我们会发现在调用Singleton::GetSingleton().Print()时会产生一个Singleton对象, 而在调用Print()时, 也会产生一个Singleton对象, 也就是说我们有了两个Singleton实例, singleton不再是singleton。那么, 为什么会这样呢。

    static Singleton& GetSingleton()

    {

        static Singleton singleton;

        return singleton;

    }

    这个函数应该只会在第一次调用时创建Singleton对象,无论如何, 不应该出现会创建两次, 调用两次构造函数的情况。对于静态变量特性理解没错(只在第一次经过时被初始化), 编译器也没问题(vc8.0),难道两次经过该静态变量是都是第一次? 那么,难道两次调用的GetSingleton函数并不是同一个函数?让我们逐一来看:

    1) Singleton::GetSingleton().Print()

    Test.exe中直接调用该函数,因为包含的头文件singleton.h有完整的实现, 在链接时会在Test.exe保存一份Singleton::GetSingleton()的实现代码。

    将其标为Singleton::GetSingleton_1();

    2) Print();

    Print()函数是从singleton.dll中导出而来的,而Print()会调用Singleton::GetSingleton(), 在链接模块singleton.dll时,因为其包含的头文件有完整的实现, 这个DLL也会保存一份Singleton::GetSingleton()的执行代码。 我将它标为Singleton::GetSingleton_2(), 虽然我们包含的是同一个头文件,两个是相同的函数名字, 但是这个函数在两个不同的模块中都存有一份独立的实现。实际上, 他们已经成为两个不同的函数了。

    看来,两个函数的确不是同一个函数。

    3       如何解决

    既然知道了原因,就会有相应的解决方法。既然我们知道有两份独立的代码分别存在于两个模块中, 那么我们要做的就是让它只有一份。最好的结果就是这个函数保存在dll中, 在Test.exe不再存有该函数的执行代码, 而是调用dll中的那个函数。现在结果很明显了:将Singleton.h编译链接singleton.dll并将外部需要使用的函数暴露出来。这样, 不管有多少模块使用到singleton, 我们始终执行singleton.dll中的代码。

    如下:

    SINGLETON_API static Singleton& GetSingleton()

    {

         static Singleton singleton;

         return singleton;

    }

    注:

    #ifdef SINGLETON_EXPORTS

    #define SINGLETON_API __declspec(dllexport)

    #else

    #define SINGLETON_API __declspec(dllimport)

    #endif

    这样在test.exe中使用该函数时,就不会再产生一个副本了,从而保证了我们的应用程序只有一个singleton

  • 相关阅读:
    Webstorm 下的Angular2.0开发之路
    利用hexo+github+nodejs搭建自我博客的一天
    滑稽的下午angularjs 2.0管道的使用
    DNGuard 标准版 v2.90发布
    DNGuard Enterprise v2.92 released
    C#复杂表达式的问题
    直接在.Net程序(C#)中执行 native code
    采用Native 引导方式的.Net加密保护
    DNGuard HVM Trial V2.82 发布
    DNGuard Enterprise v2.90 released
  • 原文地址:https://www.cnblogs.com/mokliu/p/2138792.html
Copyright © 2020-2023  润新知