我正在学习专业C++第二版的第二十九章的单例设计模式。 它说明一个Logger
类覆盖线程安全要求的单执行:发布在不同的同步上下文中使用的类成员
头
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <mutex>
// Definition of a multithread safe singleton logger class
class Logger
{
public:
static const std::string kLogLevelDebug;
static const std::string kLogLevelInfo;
static const std::string kLogLevelError;
// Returns a reference to the singleton Logger object
static Logger& instance();
// Logs a single message at the given log level
void log(const std::string& inMessage,
const std::string& inLogLevel);
// Logs a vector of messages at the given log level
void log(const std::vector<std::string>& inMessages,
const std::string& inLogLevel);
protected:
// Static variable for the one-and-only instance
static Logger* pInstance;
// Constant for the filename
static const char* const kLogFileName;
// Data member for the output stream
std::ofstream mOutputStream;
// Embedded class to make sure the single Logger
// instance gets deleted on program shutdown.
friend class Cleanup;
class Cleanup
{
public:
~Cleanup();
};
// Logs message. The thread should own a lock on sMutex
// before calling this function.
void logHelper(const std::string& inMessage,
const std::string& inLogLevel);
private:
Logger();
virtual ~Logger();
Logger(const Logger&);
Logger& operator=(const Logger&);
static std::mutex sMutex;
};
实施
#include <stdexcept>
#include "Logger.h"
using namespace std;
const string Logger::kLogLevelDebug = "DEBUG";
const string Logger::kLogLevelInfo = "INFO";
const string Logger::kLogLevelError = "ERROR";
const char* const Logger::kLogFileName = "log.out";
Logger* Logger::pInstance = nullptr;
mutex Logger::sMutex;
Logger& Logger::instance()
{
static Cleanup cleanup;
lock_guard<mutex> guard(sMutex);
if (pInstance == nullptr)
pInstance = new Logger();
return *pInstance;
}
Logger::Cleanup::~Cleanup()
{
lock_guard<mutex> guard(Logger::sMutex);
delete Logger::pInstance;
Logger::pInstance = nullptr;
}
Logger::~Logger()
{
mOutputStream.close();
}
Logger::Logger()
{
mOutputStream.open(kLogFileName, ios_base::app);
if (!mOutputStream.good()) {
throw runtime_error("Unable to initialize the Logger!");
}
}
void Logger::log(const string& inMessage, const string& inLogLevel)
{
lock_guard<mutex> guard(sMutex);
logHelper(inMessage, inLogLevel);
}
void Logger::log(const vector<string>& inMessages, const string& inLogLevel)
{
lock_guard<mutex> guard(sMutex);
for (size_t i = 0; i < inMessages.size(); i++) {
logHelper(inMessages[i], inLogLevel);
}
}
void Logger::logHelper(const std::string& inMessage,
const std::string& inLogLevel)
{
mOutputStream << inLogLevel << ": " << inMessage << endl;
}
它接着解释为什么友元类Cleanup
介绍:
Cleanup
类是确保在程序关闭时正确删除单个Logger
实例 。这是必要的,因为 这个实现动态分配Logger
实例 在受互斥锁保护的代码块中使用new运算符。 ACleanup
类的静态实例将在第一次创建 时调用instance()方法。当程序终止时,C++ 运行时将销毁此静态Cleanup
实例,这将触发 删除Logger
对象并调用Logger
析构函数 来关闭文件。
我觉得很困惑,它指出:“这是必要的,因为...”,因为如果没有其他选择。
我的问题:
1)是否真的有必要?那岂不是不够的,只是都处理在析构函数?这样的:
Logger::~Logger()
{
{
lock_guard<mutex> guard(Logger::sMutex);
delete Logger::pInstance;
Logger::pInstance = nullptr;
}
mOutputStream.close();
}
2)如果答案为1)是的,我想知道“是的,这的确是必要的!”为什么。
专业C++,第二版由马克·格雷瓜尔,尼古拉斯·Solter,斯科特J. Kleper出版社:Wrox的发布时间:2011
什么会触发Logger的析构函数? – Mat
总是释放你分配的东西,否则你会有资源泄漏。请记住,并非所有的操作系统都能为您做到这一点。 –
顺便说一下,使用Meyers的Singleton,你不需要这个(实例不是一个指针)。 – Jarod42