[英]How to delay destruction of static object (logger) in C++?
我有一個類,它將應用程序的記錄器保存為 unique_ptr。 可以通過靜態函數設置記錄器。 此外,顯然可以記錄消息。 我省略了任何線程同步(互斥鎖)以使事情變得更容易。
class LoggerWrapper {
public:
static void SetLogger(std::unique_ptr<logger::ILogger> new_logger);
static void Log(const std::string& message);
private:
static std::unique_ptr<logger::ILogger> logger_;
};
void LoggerWrapper::Log(const std::string& message) {
if (!logger_) {
// cannot log
} else {
logger_->OnLogEvent(message);
}
}
void LoggerWrapper::SetLogger(std::unique_ptr<logger::ILogger> new_logger) {
logger_ = std::move(new_logger);
}
我的問題是: unique_ptr 在應用程序中的其他一些類之前被破壞。 例如, Class Foo
的 DTOR 想要記錄一些東西,unique_ptr 可能已經被銷毀(目前就是這種情況)。 這會導致ILogger
實現被破壞,導致無法輸出日志。
有沒有人知道如何輕松解決這個問題? 我不知何故需要“延遲”靜態unique_ptr的破壞。 我還嘗試將其更改為 shared_ptr,但這只會導致 SIGABRT 出現“調用純虛擬方法”錯誤。
提前致謝!
編輯:創建了一個與我的經驗相矛盾的最小工作示例。 在這種情況下,靜態記錄器的壽命比Foo
類長。
EDIT2:我的應用程序使用exit
。 這似乎改變了破壞的順序。
EDIT3: exit
不破壞本地對象。
/******************************************************************************
Online C++ Compiler.
Code, Compile, Run and Debug C++ program online.
Write your code in this editor and press "Run" button to compile and execute it.
*******************************************************************************/
#include <iostream>
#include <memory>
#include <string>
using namespace std;
class ILogger {
public:
ILogger() {
std::cout << "ILogger CTOR" << std::endl;
}
~ILogger() {
std::cout << "ILogger DTOR" << std::endl;
}
virtual void OnLogEvent(const std::string& log_message) {
std::cout << "OnLogEvent: " << log_message << std::endl;
}
};
class LoggerWrapper {
public:
static void SetLogger(std::unique_ptr<ILogger> new_logger) {
logger_ = std::move(new_logger);
}
static void Log(const std::string& message) {
if (!logger_) {
// cannot log
} else {
logger_->OnLogEvent(message);
}
};
private:
static std::unique_ptr<ILogger> logger_;
};
class Foo {
public:
Foo(const std::string& name) : name_{name} {
LoggerWrapper::Log(name_ + ": CTOR");
}
~Foo() {
LoggerWrapper::Log(name_ + ": DTOR");
}
private:
std::string name_;
};
// declaring logger_ first causes it to be deleted AFTER foo
std::unique_ptr<ILogger> LoggerWrapper::logger_;
std::unique_ptr<Foo> foo;
int main()
{
LoggerWrapper::SetLogger(std::make_unique<ILogger>());
foo = std::make_unique<Foo>("Global FOO");
// local variables do NOT get destroyed when calling exit!
auto foo_local = Foo("Local FOO");
exit(1);
}
這是微不足道的。
首先,您不使用全局靜態對象(您不應該使用這樣的全局狀態)。 您使用函數靜態對象,以便您可以控制創建/銷毀的順序。
所以改變這個:
std::unique_ptr<ILogger> LoggerWrapper::logger_;
std::unique_ptr<Foo> foo;
進入:
class GlobalLogger
{
public:
ILogger& getLogger() {
static ILogger logger; // if you must use unique_ptr you can do that here
return logger; // But much simpler to use a normal object.
}
};
class GlobalFoo
{
public:
Foo& getFoo() {
// If there is a chance that foo is going to
// use global logger in its destructor
// then it should simply call `GlobalLogger::getLogger()`
// in the constructor of Foo. You then
// guarantee the order of creation and thus destruction.
// Alternatively, you can call it here in thus
// function just before the declaration of foo.
static Foo foo;
return foo;
}
};
// Where you were using `logger_` use `GlobalLogger::getLogger()`
// Where you were using `foo` use `GlobalFoo::getFoo()`
如果我們使用您的原始代碼作為起點,我們可以這樣做:
#include <iostream>
#include <memory>
#include <string>
// Please don't do this.
// This is the number one worst practice.
// https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice
using namespace std;
class ILogger {
public:
ILogger() {
std::cout << "ILogger CTOR" << std::endl;
}
~ILogger() {
std::cout << "ILogger DTOR" << std::endl;
}
virtual void OnLogEvent(const std::string& log_message) {
std::cout << "OnLogEvent: " << log_message << std::endl;
}
};
class LoggerWrapper
{
// Here store the logger
// as a static member of this private function.
// The the SetLogger() Log() functions get this reference.
static std::unique_ptr<ILogger>& getLogReference() {
static std::unique_ptr<ILogger> logger;
return logger;
}
public:
static void SetLogger(std::unique_ptr<ILogger> new_logger) {
// Save the new reference.
getLogReference() = std::move(new_logger);
}
// Use the logger if it has been set.
static void Log(const std::string& message) {
std::unique_ptr<ILogger>& logger_ = getLogReference();
if (!logger_) {
// cannot log
} else {
logger_->OnLogEvent(message);
}
};
};
class Foo {
public:
Foo(const std::string& name) : name_{name} {
// This calls Log()
// Which calls getLogReference()
// Which forces the creation of the function static
// variable logger so it is created before this
// object is fully initialized (if it has not already
// been created).
//
// This means this object was created after the logger
LoggerWrapper::Log(name_ + ": CTOR");
}
~Foo() {
// Because the Log() function was called in the
// constructor we know the loger was fully constructed first
// thus this object will be destroyed first
// so the logger object is guaranteed to be
// available in this objects destructor
// so it is safe to use.
LoggerWrapper::Log(name_ + ": DTOR");
}
private:
std::string name_;
};
std::unique_ptr<Foo>& globalFoo() {
// foo may destroy an object created later
// that has a destructor that calls LoggerWrapper::Log()
// So we need to call the Log function here before foo
// is created.
LoggerWrapper::Log("Initializing Global foo");
// Note: Unless somebody else has explicitly called SetLogger()
// the above line is unlikely to log anything as the logger
// will be null at this point.
static std::unique_ptr<Foo> foo;
return foo;
}
int main()
{
LoggerWrapper::SetLogger(std::make_unique<ILogger>());
globalFoo() = std::make_unique<Foo>("Global FOO");
// local variables do NOT get destroyed when calling exit!
auto foo_local = Foo("Local FOO");
exit(1);
}
創建和銷毀靜態(全局)對象的順序是未定義的。 這給你留下了幾個選擇。
shared_ptr
。 每個使用記錄器的對象都會獲得一個shared_ptr
並且在最后一個對象被銷毀后記錄器將被清理。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.