如何处理单例
How to handle a singleton?
我正在扩展一些最初不是我写的代码,并且已经使用了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
函数处理了这一点,但这一切如何与从另一个类调用Create
,然后使用Get
返回允许我调用类似Render
的成员函数的实例相联系?
我知道这远非正确,但我想做的是:
class myotherclass
{
private:
WindowManager* m_wmgr; //Window manager
}
WindowManager::Create(); //This line being the real issue here
m_wnmgr = WindowManager::Get();
m_wnmgr->Render();
调用者不需要调用Create()
。它可能隐藏在Get()
内部。如果需要创建singleton实例,它会的。这称为惰性初始化。
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()
,这确实没有问题,除非忘记了这一点,并且返回并使用了未初始化的指针。保护自己不让自己有机会忘记这样的事情是件好事。
我有一个相当大的项目,我有几个Singleton,所以这里所做的是我有了一个Singleton类对象,它是所有Singleton的基类。它有一个受保护的构造函数,所以你不能直接创建Singleton对象,但任何从它派生的类,即Singleton类型对象,都可以。这是我的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
我将展示两个派生类的外观;Logger&设置类
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* )
设置.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
设置.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基类并不依赖于任何未显示的内容。有多种方法可以实现工作的单例对象。这些类对象的功劳归于Marek A.Krzeminski,可以在www.MarekKnows.com
您确实不需要单独的"Get"方法。对静态方法"Create"的调用应返回一个指向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;
}
}
- 为什么在单例中,检查类==空?
- C++ 实现模板单例类时出现链接错误
- 在类中存储单例的指针
- C++中的单例实现在调用 getInstance 函数时不会产生相同的类实例
- 具有非默认构造函数的单例类
- 使用 std::call_once 实现类似单例的功能
- 为什么单例使用指针而不是引用?
- 提升单例池release_memory vs purge_memory
- 具有 QObject 继承的单例 - Qt
- 单例类析构函数无法清理 (SDL_Quit) MinGW
- C++单例,不会为此文档加载任何符号
- 使用 CRTP 实现单例
- C++单例模板类使我的程序崩溃
- 初学者C++线程安全单例设计
- 派生类是单例是否是一种好的做法
- _CrtIsValidHeapPointer(块)错误在应用单例-帕特恩后退出主窗口时引发
- 在这种单例实施中,是否可以对两个商店重新排序?
- 这是MSVC 2013中具有共享PTR的单例的正确实现吗?
- 为什么调用单例类 Qt 消息处理程序成员函数会出现错误:缺少参数列表
- 如何处理单例