2017-09-29 36 views
0

我正在学习专业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运算符。 A Cleanup类的静态实例将在第一次创建 时调用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

+1

什么会触发Logger的析构函数? – Mat

+0

总是释放你分配的东西,否则你会有资源泄漏。请记住,并非所有的操作系​​统都能为您做到这一点。 –

+2

顺便说一下,使用Meyers的Singleton,你不需要这个(实例不是一个指针)。 – Jarod42

回答

1

是的,这是需要在这种情况下,十月。由于本书使用了new并分发了一个指针,因此没有任何对象会超出范围,导致析构函数触发。唯一的方法是在该指针上的某个地方拨打delete。而不是要求你这样做Cleanup类是为了做到这一点。

所有这些虽然可以避免,如果你使用迈尔斯单身。它使用单例类型的静态变量并返回一个指针/引用。与书籍版本不同,该程序结束时会自动销毁。一个Meyers Singleton看起来像:

class Singleton { 
public: 
    static Singleton* Instance() { static Singleton s; return &s; } 
    Singleton(const Singleton&) = delete; 
    void operator=(const Singleton&) = delete; 
private: 
    Singleton() = default; 
}; 
+0

你回答让我想知道这本被推荐的书是否有用。我的意思是,通过比较其提议的单体变体与Meyer的替代方法,我学到了很多东西,但是当需要快速参考设计模式时,是否有一本书以最接受的形式记录了实现? – levelont

+0

@levelont不知道,但[这里](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list)是一个开始搜索的好地方 – NathanOliver

相关问题