C ++中的单例实现

时间:2017-07-01 22:14:34

标签: c++ oop singleton

我正在尝试在C ++中实现单例。我对这门语言比较陌生,所以请解释一下我是否犯了任何小错误。

以下是我的标题文件:

#pragma once
#include <glad\glad.h>
#include <GLFW\glfw3.h>
#include "Imgui\imgui.h"
#include "imgui_impl_glfw_gl3.h"


class GUI
{

    static GUI * instance;

private:

    GUI();

public:

    static GUI * Instance();

    void Init(GLFWwindow * window);
    void Loop();
    void Draw();
    void End();

    ~GUI();
};

和Class文件的一部分。

GUI::GUI()
{
    instance = nullptr;
    // static GUI * instance = nullptr;
}


GUI * GUI::Instance()
{
    if (nullptr == instance) {
        instance = new GUI();
    }

    return instance;
}

我觉得它应该可行,但是,当我尝试执行时,我收到了这个错误。

Error Image.

我对此很新,所以任何有关我错误的帮助都会受到赞赏。

3 个答案:

答案 0 :(得分:1)

实现单例的更简单方法是

GUI * GUI::Instance()
{

    static GUI* instance = new GUI();
    retune instance;

}

这样做,您不必在GUI类中设置任何字段。

注意:如果您想使用引用而不是指针,请将上面的代码中的星号(*)替换为&符号(&amp;)。

答案 1 :(得分:1)

使用OOP设计和讨论Singleton类型时:主要目标是Singleton是一种Object,在程序或应用程序的运行时间内,您只需要1个对象实例。我通常做的是创建一个Singleton类,它是所有单例类型将继承的基类。我不会显示所有派生类,但我将展示Singleton类:

<强> Singleton.h

#ifndef SINGLETON_H
#define SINGLETON_H

class Singleton {
public:
    // Number Of Items In Enum Type Must Match The Number
    // Of Items And Order Of Items Stored In s_aSingletons
    enum SingletonType {
        TYPE_LOGGER = 0, // MUST BE FIRST!
        TYPE_SETTINGS,
        TYPE_ENGINE,
        TYPE_ANIMATION_MANAGER,
        TYPE_SHADER_MANAGER,
        TYPE_ASSET_STORAGE,
        TYPE_AUDIO_MANAGER,
        TYPE_FONT_MANAGER,
        TYPE_BATCH_MANAGER,     
    }; // Type

private:
    SingletonType m_eType;

public:
    virtual ~Singleton();

protected:
    explicit Singleton( SingletonType eType );

    void logMemoryAllocation( bool isAllocated ) const;

private:
    Singleton( const Singleton& c ); // Not Implemnted
    Singleton& operator=( const Singleton& c ); // Not Implemented
}; // Singleton

#endif // SINGLETON_H

<强> Singleton.cpp

#include "Singleton.h"

#include "Logger.h"
#include "Settings.h"

struct SingletonInfo {
    const std::string strSingletonName;
    bool              isConstructed;

    SingletonInfo( const std::string& strSingletonNameIn ) :
        strSingletonName( strSingletonNameIn ),
        isConstructed( false )
    {}
}; // SingletonInfo

// Order Must Match Types Defined In Singleton::SingeltonType enum
static std::array<SingletonInfo, 9> s_aSingletons = { SingletonInfo( "Logger" ),
                                                      SingletonInfo( "Settings" ),
                                                      SingletonInfo( "Engine" ),
                                                      SingletonInfo( "AnimationManager" ),
                                                      SingletonInfo( "ShaderManager" ),
                                                      SingletonInfo( "AssetStorage" ),                                                  
                                                      SingletonInfo( "AudioManager" ), 
                                                      SingletonInfo( "FontManager" ),
                                                      SingletonInfo( "BatchManager" ) };

// ----------------------------------------------------------------------------
// Singleton()
Singleton::Singleton( SingletonType eType ) :
m_eType( eType ) {
    bool bSaveInLog = s_aSingletons.at( TYPE_LOGGER ).isConstructed;

    try {
        if ( !s_aSingletons.at( eType ).isConstructed ) {
            // Test Initialization Order
            for ( int i = 0; i < eType; ++i ) {
                if ( !s_aSingletons.at( i ).isConstructed ) {
                    throw ExceptionHandler( s_aSingletons.at( i ).strSingletonName + " must be constructued before constructing " + s_aSingletons.at( eType ).strSingletonName, bSaveInLog );
                }
            }
            s_aSingletons.at( eType ).isConstructed = true;

            if ( s_aSingletons.at( TYPE_ENGINE ).isConstructed && 
                 Settings::get()->isDebugLoggingEnabled( Settings::DEBUG_MEMORY ) ) {
                logMemoryAllocation( true );
            }

        } else {
            throw ExceptionHandler( s_aSingletons.at( eType ).strSingletonName + " can only be constructed once.", bSaveInLog );
        }
    } catch ( std::exception& ) {
        // eType Is Out Of Range
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " Invalid Singelton Type sepcified: " << eType;
        throw ExceptionHandler( strStream, bSaveInLog );
    }
} // Singleton

// ----------------------------------------------------------------------------
// ~Singleton()
Singleton::~Singleton() {
    if ( s_aSingletons.at( TYPE_ENGINE ).isConstructed &&
         Settings::get()->isDebugLoggingEnabled( Settings::DEBUG_MEMORY ) ) {
        logMemoryAllocation( false );
    }
    s_aSingletons.at( m_eType ).isConstructed = false;
} // ~Singleton

// ----------------------------------------------------------------------------
// logMemoryAllocation()
void Singleton::logMemoryAllocation( bool isAllocated ) const {
    if ( isAllocated ) {
        Logger::log( "Created " + s_aSingletons.at( m_eType ).strSingletonName );
    } else {
        Logger::log( "Destroyed " + s_aSingletons.at( m_eType ).strSingletonName );
    }
} // logMemoryAllocation

现在上面的类不会直接编译,因为它依赖于来自大型库的其他类对象,但是这是为了演示如何设计或实现Singleton而显示的。

从上面的类声明中可以看出,这个库中有不同类型的对象,这些单元都可以在类的枚举类型中看到,也可以在SingletonInfo对象的静态数组中看到,这是一个包含信息的结构关于在* .cpp文件中找到的Singleton。

所有这些类型:

  • Logger
  • Settings
  • Engine
  • AnimationManager
  • ShaderManager
  • AssetStorage
  • AudioManager
  • FontManager
  • BatchManager

所有单身人士和我的图书馆中只有1个这样的类类型的实例,因为它们都是从上面的类继承的。基类还负责构造这些对象的顺序,因为在创建另一个对象之前可能需要先存在某个对象,并且该类还确保一旦构造​​了一个对象,就不能再构造该类的另一个实例;它会引发异常。这个类也是线程安全的,因为有几个派生类正在使用多线程进程。

现在,获取类的静态指针的static get()方法;我没有在基类中实现它。作为单例的每个Derived类实现了它自己的方法,用于返回一个静态指针,该指针在成功创建时设置为它的this指针。

现在,对于从此继承的派生类,它的静态指针方法看起来就像这样。

<强> Derived.h

#include "Singleton.h"

#ifndef DERIVED_H
#define DERIVED_H

class Derived final : public Singleton {
public:
    static Derived* const get();

    /*Data Type*/ doSomething( /* ... */ ) { /* ... */ }
};

#endif // DERIVED_H

<强> Derived.cpp

#include "Derived.h"

static Settings* s_pSettings = nullptr;

Settings::Settings() :
Singleton( TYPE_DERIVED ) { // Must also be in the enumerated type and the static array
    s_pSettings = this;
}

现在使用这个派生类型:

main.cpp

#include "Derived.h"
#include "SomeOtherClass.h"

int main() {
    Derived derived; // Create your singleton Instance.

    SomeOtherClass soc( /*some data to initialize or construct*/ );

    soc.someFunc( /* ... */ );

    return 0;
}

假设SomeOtherClass使用Derived并且需要访问其静态指针,这就是人们如何做到的:

<强> SomeOtherClass.h

#ifndef SOMEOTHERCLASS_H
#define SOMEOTHERCLASS_H

class SomeOtherClass {
public:
    SomeOtherClass( /* ... */ );

     void someFunc( /* ... */ );
    // Class Details Here                   
};

#endif // SOMEOTHER_CLASS_H

<强> SomeOtherClass.cpp

#include "SomeOtherClass.h"
#include "Derived.h"

static Derived* s_pDerived = nullptr;

SomeOtherClass::SomeOtherClass( /* ... */ ) {
    s_pDerived = Derived::get(); // Only If Derived Was Already Created First
}

void SomeOtherClass::someFunc( /* ... */ ) {
    s_pDerived->doSomething( /* ... */ );     
}

现在,对于动态分配的这些派生单例类型的指针,我更喜欢使用std::shared_ptr<>std:unique_ptr<>,具体取决于我的需要。这有助于避免内存泄漏,并且还提供了一种处理动态对象访问和所有权的好方法。

答案 2 :(得分:0)

您需要在标头和实现文件中声明静态实例变量,即

GUI * GUI::instance;

需要位于implementation(cpp)文件中的某个位置。