释放在不同同步上下文中使用的类成员

时间:2017-09-29 11:45:34

标签: c++ multithreading memory-management mutex

我正在通过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)的答案是“是的,那确实是必要的!”,我想知道原因。

1 专业C ++,第二版由Marc Gregoire,Nicholas A. Solter,Scott J. Kleper发布者:Wrox发布日期:2011年10月

1 个答案:

答案 0 :(得分:1)

是的,在这种情况下需要这样做。由于本书使用了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;
};