如何处理单身人士?

时间:2015-12-12 02:38:14

标签: c++ singleton

我正在扩展一些原本并非由我编写的代码,并且已经使用了单例(尽管从我所读过的内容可能并未完全正确实现)。无论如何,单身人士本身不应该改变,我不会想。这就是我所拥有的:

class WindowManager
{
    private:
        static WindowManager*           s_wndmgr; //A singleton maintains a pointer to itself as a class variable

        //To ensure the integrity of a singleton
        //Constructors and the destructor should remain private
        WindowManager();
        ~WindowManager();

    public:
        static void Create();
        static void Destroy();
        static inline WindowManager* Get()
        {
            return s_wndmgr;
        }
        void Render();
}
WindowManager* WindowManager::s_wndmgr = NULL;

WindowManager::WindowManager()
{
    s_wndmgr = NULL;
}

WindowManager::~WindowManager()
{
    //Cleanup other stuff if necessary

    delete s_wndmgr;
}

void WindowManager::Create()
{
    if ( !s_wndmgr ) s_wndmgr = new WindowManager();
}

void WindowManager::Destroy()
{
    if ( s_wndmgr ) delete s_wndmgr;
}

我之前没有接触过单身人士,而且我对C ++本身也很陌生。对我来说,我习惯于实例化将调用构造函数的类,但在这种情况下我可以看到Create函数处理它,但是这一切如何与调用{{1}相关联从另一个类,然后使用Create返回实例,允许我调用像Get这样的成员函数?

我知道这远非正确,但这是我想要做的事情:

Render

3 个答案:

答案 0 :(得分:2)

来电者无需拨打Create()。它可以隐藏在Get()内。如果需要创建单例实例,它将会。这称为延迟初始化。

static inline WindowManager* Get()
{
    Create();
    return s_wndmgr;
}

您可以完全删除Create()方法并将其移至Get()

static inline WindowManager* Get()
{
    if ( !s_wndmgr ) s_wndmgr = new WindowManager();
    return s_wndmgr;
}

或者更好的是,摆脱私有指针并返回引用而不是指针:

static inline WindowManager& Get()
{
    static WindowManager instance;
    return instance;
}

然后,就这样使用它:

m_wnmgr = WindowManager::Get();
m_wnmgr.Render();

调用者不必像你发布的代码一样调用Create(),除非忘记它并返回并使用未初始化的指针。保护自己,甚至有机会忘记这样的事情,这是件好事。

答案 1 :(得分:1)

我有一个相当大规模的项目,我有几个单身,所以这里做的是我有一个Singleton类对象,它是所有单例的基类。它有一个受保护的构造函数,因此你不能直接创建一个Singleton对象,但任何从它派生的类是Singleton Type Object。这是我的Singleton类的声明和定义。

<强> Singleton.h

#ifndef SINGLETON_H
#define SINGLETON_H

class Singleton {
public:
    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 Implemented
    Singleton& operator=( const Singleton& c ); // Not Implemented

}; // Singleton

#endif // SINGLETON_H

<强> Singleton.cpp

#include "stdafx.h"
#include "Logger.h"
#include "Singleton.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::SingletonType 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 constructed 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 Singleton Type Specified: " << 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

我将展示2个派生类的外观;记录器&amp;设置类

<强> Logger.h

#ifndef LOGGER_H
#define LOGGER_H

#include "Singleton.h"

class Logger sealed : public Singleton {
public:
    // Number Of Items In Enum Type Must Match The Number Of Items
    // And Order Of Items Stored In s_aLogTypes
    enum LoggerType {
        TYPE_INFO = 0,
        TYPE_WARNING,
        TYPE_ERROR,
        TYPE_CONSOLE,
    }; // Type

private:
    std::string m_strLogFilename;
    unsigned    m_uMaxCharacterLength;

    std::array<std::string, 4>  m_aLogTypes;
    const std::string           m_strUnknownLogType;

    HANDLE m_hConsoleOutput;
    WORD   m_consoleDefaultColor;

public:
    explicit Logger( const std::string& strLogFilename );
    virtual ~Logger();

    static void log( const std::string& strText, LoggerType eLogType = TYPE_INFO );
    static void log( const std::ostringstream& strStreamText, LoggerType eLogType = TYPE_INFO );
    static void log( const char* szText,  LoggerType eLogType = TYPE_INFO );

private:
    Logger( const Logger& c ); // Not Implemented
    Logger& operator=( const Logger&  c ); // Not Implemented

}; // Logger

#endif // LOGGER_H

<强> Logger.cpp

#include "stdafx.h"
#include "Logger.h"
#include "BlockThread.h"
#include "TextFileWriter.h"

static Logger* s_pLogger = nullptr;
static CRITICAL_SECTION s_criticalSection;

static const WORD WHITE_ON_RED = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY | BACKGROUND_RED; // White Text On Red Background

// ----------------------------------------------------------------------------
// Logger()
// Initialize A File To Be Used For Logging
Logger::Logger( const std::string& strLogFilename ) :
Singleton( TYPE_LOGGER ),
m_strLogFilename( strLogFilename ),
m_uMaxCharacterLength( 0 ),
m_strUnknownLogType( "UNKNOWN" ) {

    // Oder Must Match Types Defined In Logger::Type enum
    m_aLogTypes[0] = "Info";
    m_aLogTypes[1] = "Warning";
    m_aLogTypes[2] = "Error";
    m_aLogTypes[3] = ""; // Console

    // Find Widest Log Type String
    m_uMaxCharacterLength = m_strUnknownLogType.size();
    for each( const std::string& strLogType in m_aLogTypes ) {
        if ( m_uMaxCharacterLength < strLogType.size() ) {
             m_uMaxCharacterLength = strLogType.size();
        }
    }

    InitializeCriticalSection( &s_criticalSection );
    BlockThread blockTread( s_criticalSection ); // Enter Critical Section

    // Start Log File
    TextFileWriter file( m_strLogFilename, false, false );

    // Prepare Console
    m_hConsoleOutput = GetStdHandle( STD_OUTPUT_HANDLE );

    CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
    GetConsoleScreenBufferInfo( m_hConsoleOutput, &consoleInfo );
    m_consoleDefaultColor = consoleInfo.wAttributes;

    s_pLogger = this;

    logMemoryAllocation( true );

} // Logger

// ----------------------------------------------------------------------------
// ~Logger()
Logger::~Logger() {
    logMemoryAllocation( false );

    s_pLogger = nullptr;

    DeleteCriticalSection( &s_criticalSection );
} // ~Logger

// ----------------------------------------------------------------------------
// log( const std::string )
void Logger::log( const std::string& strText, LoggerType eLogType ) {
    log( strText.c_str(), eLogType );
} // log( const std::string )

// ----------------------------------------------------------------------------
// log( const std::ostringstream )
void Logger::log( const std::ostringstream& strStreamText, LoggerType eLogType ) {
    log( strStreamText.str().c_str(), eLogType );
} // log( const std::ostringstream )

// ----------------------------------------------------------------------------
// log( const char* )
void Logger::log( const char* szText, LoggerType eLogType ) {

    if ( nullptr == s_pLogger ) {
        std::cout << "Logger has not been initialized, can not log " << szText << std::endl;
        return;
    }

    BlockThread blockThread( s_criticalSection ); // Enter Critical Section

    std::ostringstream strStream;

    // Default White Text On Red Background
    WORD textColor = WHITE_ON_RED;

    // Chose Log Type Text String, Display "UNKNOWN" If eLogType Is Out Of Range
    strStream << std::setfill(' ') << std::setw( s_pLogger->m_uMaxCharacterLength );

    try {
        if ( TYPE_CONSOLE != eLogType ) {
            strStream << s_pLogger->m_aLogTypes.at( eLogType );
        }
        if ( TYPE_WARNING == eLogType ) {
            // Yellow
            textColor = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_GREEN;
        } else if ( TYPE_INFO == eLogType ) {
            // Green
            textColor = FOREGROUND_GREEN;
        } else if ( TYPE_CONSOLE == eLogType ) {
            // Cyan
            textColor = FOREGROUND_GREEN | FOREGROUND_BLUE;
        }
    } catch( ... ) {
        strStream << s_pLogger->m_strUnknownLogType;
    }

    // Date And Time
    if ( TYPE_CONSOLE != eLogType ) {
        SYSTEMTIME time;
        GetLocalTime( &time );

        strStream << " [" << time.wYear << "."
            << std::setfill('0') << std::setw( 2 ) << time.wMonth << "."
            << std::setfill('0') << std::setw( 2 ) << time.wDay << " "
            << std::setfill(' ') << std::setw( 2 ) << time.wHour << ":"
            << std::setfill('0') << std::setw( 2 ) << time.wMinute << ":"
            << std::setfill('0') << std::setw( 2 ) << time.wSecond << "."
            << std::setfill('0') << std::setw( 3 ) << time.wMilliseconds << "] ";
    }
    strStream << szText << std::endl;

    // Log Message
    SetConsoleTextAttribute( s_pLogger->m_hConsoleOutput, textColor );
    std::cout << strStream.str();

    // Save Message To Log File
    try {
        TextFileWriter file( s_pLogger->m_strLogFilename, true, false );
        file.write( strStream.str() );

    } catch( ... ) {
        // Not Saved In Log File, Write Message To Console
        std::cout << __FUNCTION__ << " failed to write to file: " << strStream.str() << std::endl;
    }

    // Reset To Default Color
    SetConsoleTextAttribute( s_pLogger->m_hConsoleOutput, s_pLogger->m_consoleDefaultColor );

} // log( const char* )

<强> Settings.h

#ifndef SETTINGS_H
#define SETTINGS_H

#include "Singleton.h"

class Settings sealed : public Singleton {
public:
    enum DebugLogging {
        DEBUG_NONE      = 0,
        DEBUG_MEMORY    = ( 1 << 0 ),
        DEBUG_RENDER    = ( 1 << 1 ),
        DEBUG_GUI       = ( 1 << 2 ),

        DEBUG_ALL       = 0xffffffff
    }; // DebugLogging

private:
    unsigned        m_uPhysicsRefreshRateHz;
    bool            m_isWindowedMode;
    unsigned long   m_uRandNumGenSeed;
    glm::uvec2      m_gamePixelSize;
    glm::uvec2      m_openglVersion;
    DebugLogging    m_eDebugLogging;

public:
    static Settings* const get();

    Settings();
    virtual ~Settings();

    std::string getNameAndVersion() const;

    void    setRandomNumberSeed( unsigned long uSeedValue );

    void    setWindowDisplayMode( bool isWindowed );
    bool    isWindowDisplayMode() const;

    void            setDebugLogging( DebugLogging eDebugLogging );
    void            setDebugLogging( unsigned uDebugLogging );
    DebugLogging    getDebugLogging() const;
    bool            isDebugLoggingEnabled( DebugLogging eDebugLogging ) const;

    void                setGameSize( const glm::uvec2& uGamePixelSize );
    const glm::uvec2&   getGameSize() const;

    double  getPhysicsStepSeconds() const;

    std::string showSummary() const;

    void                setOpenglVersion( const glm::uvec2& version );
    const glm::uvec2&   getOpenglVersion() const;

private:
    Settings( const Settings& c ); // Not Implemented
    Settings& operator=( const Settings& c ); // Not Implemented

}; // Settings

#endif // SETTINGS_H

<强> Settings.cpp

#include "stdafx.h"
#include "Settings.h"
#include "BuildConfig.h"

static Settings* s_pSettings = nullptr;

// ----------------------------------------------------------------------------
// get()
Settings* const Settings::get() {
    if ( nullptr == s_pSettings ) {
        throw ExceptionHandler( __FUNCTION__ + std::string( " failed, Settings has not been constructed yet" ) );
    }
    return s_pSettings;
} // get

// ----------------------------------------------------------------------------
// Settings()
Settings::Settings() :
Singleton( TYPE_SETTINGS ),
m_uPhysicsRefreshRateHz( 100 ), // Should Not Be Less Then 24Hz
m_isWindowedMode( false ),
m_uRandNumGenSeed( 0 ),
m_gamePixelSize( 1024, 768 ),
m_openglVersion( 0, 0 ),
m_eDebugLogging( DEBUG_NONE ) {
    s_pSettings = this;

    logMemoryAllocation( true );
} // Settings

// ----------------------------------------------------------------------------
// ~Settings()
Settings::~Settings() {
    logMemoryAllocation( false );
    s_pSettings = nullptr;
} // ~Settings

// ----------------------------------------------------------------------------
// getNameAndVersion()
std::string Settings::getNameAndVersion() const {
    std::ostringstream strStream;
    strStream << g_strGameName
        << " v" << g_iMajorVersion << "." << g_iMinorVersion << "." << g_iBuildNumber;
    return strStream.str();
} // getNameAndVersion

// ----------------------------------------------------------------------------
// setRandomNumberSeed()
void Settings::setRandomNumberSeed( unsigned long uSeedValue ) {
    m_uRandNumGenSeed = uSeedValue;
} // setRandomNumberSeed

// ----------------------------------------------------------------------------
// setWindowDisplayMode()
void Settings::setWindowDisplayMode( bool isWindowed ) {
    m_isWindowedMode = isWindowed;
} // setWindowDisplayMode

// ----------------------------------------------------------------------------
// isWindowDisplayMode()
bool Settings::isWindowDisplayMode() const {
    return m_isWindowedMode;
} // isWindowDisplayMode

// ----------------------------------------------------------------------------
// setDebugLogging()
void Settings::setDebugLogging( DebugLogging eDebugLogging ) {
    m_eDebugLogging = eDebugLogging;
} // setDebugLogging

// ----------------------------------------------------------------------------
// setDebugLogging()
void Settings::setDebugLogging( unsigned uDebugLogging ) {
    m_eDebugLogging = static_cast<Settings::DebugLogging>( uDebugLogging );
} // setDebugLogging

// ----------------------------------------------------------------------------
// getDebugLogging()
Settings::DebugLogging Settings::getDebugLogging() const {
    return m_eDebugLogging;
} // getDebugLogging

// ----------------------------------------------------------------------------
// isDebugLoggingEnabled()
bool Settings::isDebugLoggingEnabled( DebugLogging eDebugLogging ) const {
    return ( (m_eDebugLogging & eDebugLogging ) > 0 );
} // isDebugLoggingEnabled

// ----------------------------------------------------------------------------
// setGameSize()
void Settings::setGameSize( const glm::uvec2& uGamePixelSize ) {
    m_gamePixelSize = glm::uvec2( glm::clamp( uGamePixelSize.x, 800U, 2048U ),
                                  glm::clamp( uGamePixelSize.y, 600U, 2048U ) );
} // setGameSize

// ----------------------------------------------------------------------------
// getGameSize
const glm::uvec2& Settings::getGameSize() const {
    return m_gamePixelSize;
} // getGameSize

// ----------------------------------------------------------------------------
// getPhysicsStepSeconds()
double Settings::getPhysicsStepSeconds() const {
    return ( 1.0 / static_cast<double>( m_uPhysicsRefreshRateHz ) );
} // getPhysicsStepSeconds

// ----------------------------------------------------------------------------
// showSummary()
std::string Settings::showSummary() const {
    int iWidth = 53;
    std::ostringstream strStream;
    strStream << "Game Settings: " << std::endl;

    // OpenGL Version
    strStream << std::setfill(' ') << std::setw( iWidth ) << "OpenGL: " << m_openglVersion.x << "." << m_openglVersion.y << std::endl;

    // Random Number Generator Seed Value
    strStream << std::setfill(' ') << std::setw( iWidth ) << "Seed Value: " << m_uRandNumGenSeed << std::endl;

    // Render Mode And Size
    strStream << std::setfill(' ') << std::setw( iWidth ) << "Render Mode: " << ( m_isWindowedMode ? "Window" : "Full Screen" ) << std::endl;
    strStream << std::setfill(' ') << std::setw( iWidth ) << "Game Screen Resolution: " << m_gamePixelSize.x << "x" << m_gamePixelSize.y << " pixels" << std::endl;

    // Refresh Settings
    strStream << std::setfill(' ') << std::setw( iWidth ) << "Physics Refresh: " << m_uPhysicsRefreshRateHz << " Hz" << std::endl;

    return strStream.str();
} // showSummary

// ----------------------------------------------------------------------------
// setOpenglVersion()
void Settings::setOpenglVersion( const glm::uvec2& version ) {
    if ( version.x < 2 ) {
        // Using Older OpenGL 1.x
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " " << g_strGameName << " requires OpenGL v2+ to be supported";
        throw ExceptionHandler( strStream );
    }

    m_openglVersion = version;
} // setOpenglVersion

// ----------------------------------------------------------------------------
// getOpenglVersion()
const glm::uvec2& Settings::getOpenglVersion() const {
    return m_openglVersion;
} // getOpenglVersion

显然,这不会在你的机器上构建或编译,因为其中一些类依赖于此处未显示的其他类。但是,Singleton Base类不依赖于未显示的任何内容。有多种方法可以实现工作单例对象。这些类对象的归功于Marek A. Krzeminski,可在www.MarekKnows.com

找到

答案 2 :(得分:0)

你真的不需要一个单独的&#34; Get&#34;方法。对静态方法的调用&#34;创建&#34;应返回一个指向WindowManager实例的指针(如果不存在则将创建)

class WindowManager
{
    private:
        static WindowManager*           s_wndmgr; //A singleton maintains a pointer to itself as a class variable

        //To ensure the integrity of a singleton
        //Constructors and the destructor should remain private
        WindowManager();
        ~WindowManager();

    public:
        static WindowManager* Create();
        static void Destroy();
        void Render();
}

WindowManager* WindowManager::s_wndmgr = NULL;

WindowManager::WindowManager()
{
}

WindowManager::~WindowManager()
{
}

WindowManager* WindowManager::Create()
{
    if ( !s_wndmgr ) 
    {
        s_wndmgr = new(std::nothrow) WindowManager();
    }

    return s_wndmgr;
}

void WindowManager::Destroy()
{
    if ( s_wndmgr )
    {
     delete s_wndmgr;
     s_wndmgr = NULL;
    }
}