我正在使用Professional C ++第二版1的第29章学习单例设计模式。
它说明了一个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运算符。一种
  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)的回答是“是的,的确有必要!”,我想知道为什么。

1Professional C ++,第二版,作者马克·格里高(Marc Gregoire),尼古拉斯·索尔特(Nicholas A. Solter),斯科特·J·克莱珀(Scott J. Kleper)发行者:Wrox出版日期:2011年10月

最佳答案

是的,在这种情况下需要这样做。由于这本书使用了new并分发了一个指针,因此不会有任何对象超出范围而导致析构函数触发。唯一的方法是在该指针的某个位置调用delete。无需要求您创建Cleanup类即可。

但是,如果使用Meyers Singleton,可以避免所有这些情况。它使用单例类型的静态变量并返回对此的指针/引用。与书籍版本不同,该变量将在程序结束时自动销毁。迈耶·辛格尔顿(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;
};

关于c++ - 释放用于不同同步上下文的类成员,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/46488136/

10-12 20:35