释放在不同同步上下文中使用的 class 个成员
Releasing class members used in different synchronization contexts
我正在学习专业 C++ 第二版第 29 章的单例设计模式1。
它说明了涵盖线程安全要求的 Logger
class 的单例实现:
页眉
#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;
}
接着解释为什么介绍朋友classCleanup
:
The Cleanup
class is there to make sure the single Logger
instance
gets deleted properly on program shutdown. This is necessary because
this implementation is dynamically allocating the Logger
instance by
using the new operator in a block of code protected with a mutex. A
static instance of the Cleanup
class will be created the first time
the instance() method is called. When the program terminates, the C++
runtime will destroy this static Cleanup
instance, which will trigger
the deletion of the Logger
object and a call to the Logger
destructor
to close the file.
我觉得很困惑,它说“这是必要的,因为...”,好像别无选择。
我的问题:
1) 真的有必要吗?仅在析构函数中进行所有处理是否就足够了?例如:
Logger::~Logger()
{
{
lock_guard<mutex> guard(Logger::sMutex);
delete Logger::pInstance;
Logger::pInstance = nullptr;
}
mOutputStream.close();
}
2)如果1)的答案是"yes, it is indeed necessary!",我想知道为什么。
1Professional C++,第二版,作者:Marc Gregoire、Nicholas A. Solter、Scott J. Kleper 出版商:Wrox 出版日期:2011 年 10 月
是的,在这种情况下需要这样做。由于本书使用 new
并分发了一个指针,因此没有对象会超出范围导致析构函数触发。这样做的唯一方法是在该指针的某处调用 delete
。 Cleanup
class 不是要求你这样做的,而是为了做到这一点而创建的。
如果使用 Meyers Singleton,所有这些都可以避免。它使用单例类型的静态变量和 returns a pointer/reference 。与书本版本不同,它会在程序结束时自动销毁。 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++ 第二版第 29 章的单例设计模式1。
它说明了涵盖线程安全要求的 Logger
class 的单例实现:
页眉
#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;
}
接着解释为什么介绍朋友classCleanup
:
The
Cleanup
class is there to make sure the singleLogger
instance gets deleted properly on program shutdown. This is necessary because this implementation is dynamically allocating theLogger
instance by using the new operator in a block of code protected with a mutex. A static instance of theCleanup
class will be created the first time the instance() method is called. When the program terminates, the C++ runtime will destroy this staticCleanup
instance, which will trigger the deletion of theLogger
object and a call to theLogger
destructor to close the file.
我觉得很困惑,它说“这是必要的,因为...”,好像别无选择。
我的问题:
1) 真的有必要吗?仅在析构函数中进行所有处理是否就足够了?例如:
Logger::~Logger()
{
{
lock_guard<mutex> guard(Logger::sMutex);
delete Logger::pInstance;
Logger::pInstance = nullptr;
}
mOutputStream.close();
}
2)如果1)的答案是"yes, it is indeed necessary!",我想知道为什么。
1Professional C++,第二版,作者:Marc Gregoire、Nicholas A. Solter、Scott J. Kleper 出版商:Wrox 出版日期:2011 年 10 月
是的,在这种情况下需要这样做。由于本书使用 new
并分发了一个指针,因此没有对象会超出范围导致析构函数触发。这样做的唯一方法是在该指针的某处调用 delete
。 Cleanup
class 不是要求你这样做的,而是为了做到这一点而创建的。
如果使用 Meyers Singleton,所有这些都可以避免。它使用单例类型的静态变量和 returns a pointer/reference 。与书本版本不同,它会在程序结束时自动销毁。 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;
};