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