[英]Best Way To Destruct Global And Static Objects?
結束具有靜態存儲持續時間的對象的生命周期的最佳方法是什么?
當前實現找到__run_exit_handlers
的調用者,然后將用於確定__exit_funcs
。
然而,這很容易失敗,因為即使在具有相同版本的 glibc 中,對__run_exit_handlers
偏移也很容易改變。 可以做的另一件事是首先解析__run_exit_handlers
的地址,然后使用它來查找調用者,而不是使用硬編碼的調用偏移量。
#include <iostream>
#include <fstream>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <cstdio>
#include <execinfo.h>
struct A
{
A(std::string pName)
: mName(pName)
{
std::printf("%s %s\n", __PRETTY_FUNCTION__, mName.c_str());
}
~A()
{
std::printf("%s %s\n", __PRETTY_FUNCTION__, mName.c_str());
}
volatile int i = 0;
std::string mName;
};
A a{"a"};
A b{"b"};
A c{"c"};
class StaticDestroyer
{
public:
StaticDestroyer()
{
std::ifstream maps("/proc/self/maps", std::ios::in);
char line[1024];
uint8_t* magic = nullptr;
while (maps.getline(line, sizeof(line)))
{
char perms[4];
uint8_t *magicbegin, *magicend;
std::string lsv(line);
if (std::string::npos == lsv.find("/libc-",0,6)) continue;
std::sscanf(line, "%lx-%lx %4s", &magicbegin, &magicend, perms);
if (perms[0]==114 && perms[2]==120)
{
magic = findMagic(magicbegin, magicend);
break;
}
}
if (magic==nullptr)
throw std::runtime_error("magic not found!");
mHead = *(HakaishinNode**)magic;
}
bool destroy(void* pTarget)
{
HakaishinNode *current = mHead;
while (nullptr != current)
{
for (size_t i = current->idx-1 ; i>0; i--)
{
const Hakaishin *const f = ¤t->fns[i];
if (4 == f->type && pTarget == f->arg)
{
void (*destruct) (void *arg, int status) = f->fn;
asm ("ror $2*8+1, %0\nxor %%fs:%c2, %0" : "=r" (destruct) : "0" (destruct), "i" (48));
destruct (f->arg, 1);
if (current->idx-1 != i) for (size_t j = i; j < current->idx ; j++) current->fns[j] = current->fns[j+1];
current->idx--;
return true;
}
}
current = current->next;
}
return false;
}
private:
struct Hakaishin
{
long int type;
void (*fn) (void *arg, int status);
void *arg;
void *dso_handle;
};
struct HakaishinNode
{
struct HakaishinNode *next;
size_t idx;
Hakaishin fns[32];
};
uint8_t* findMagic(uint8_t* magicbegin, uint8_t* magicend)
{
const void* const begin = magicbegin;
int32_t ptr;
while ((magicbegin+7) <= magicend)
{
if (magicbegin[0]==0x48 && (magicbegin[1]==0x8b || magicbegin[1]==0x8d))
{
std::memcpy(&ptr, magicbegin+3, sizeof(ptr));
uint8_t* magicp = magicbegin+ptr+7;
if (ptr==0x38a5c1) return magicp;
}
magicbegin++;
}
return nullptr;
}
HakaishinNode* mHead = nullptr;
};
A& getA()
{
static A a{"getA"};
return a;
}
A& getA2()
{
static A a{"getA2"};
return a;
}
int main()
{
std::printf("entering...\n");
StaticDestroyer d;
d.destroy(&a);
d.destroy(&b);
auto& ga = getA();
d.destroy(&ga);
getA2();
std::printf("returning...\n");
}
輸出:
A::A(std::string) a
A::A(std::string) b
A::A(std::string) c
entering...
A::~A() a
A::~A() b
A::A(std::string) getA
A::~A() getA
A::A(std::string) getA2
returning...
A::~A() getA2
A::~A() c
靜態對象將隨着程序的終止而被破壞。
如果您喜歡管理資源,請不要將其設為靜態或使用靜態指針。 您可以在此處分配和取消分配相應的資源。 這種方法非常接近單例,它被認為是一種反模式。
結論:如果您需要管理資源,請不要將其設為靜態。
需要以這種方式處理生命周期的默認行為表明您的應用程序存在設計缺陷。
因此,您應該考慮重組您的程序以不使用全局變量。 或者至少改變你處理全局變量的方式。 所以如果你真的需要全局變量並提前釋放它們,那么切換到unique_ptr
:
#include <iostream>
#include <functional>
#include <memory>
struct A
{
A(std::string pName)
: mName(pName)
{
std::printf("%s %s\n", __PRETTY_FUNCTION__, mName.c_str());
}
~A()
{
std::printf("%s %s\n", __PRETTY_FUNCTION__, mName.c_str());
}
volatile int i = 0;
std::string mName;
};
auto a = std::make_unique<A>("a");
auto b = std::make_unique<A>("b");
auto c = std::make_unique<A>("c");
auto& getA()
{
static auto a = std::make_unique<A>("getA");
return a;
}
auto& getA2()
{
static auto a = std::make_unique<A>("getA2");
return a;
}
int main() {
std::printf("entering...\n");
a = nullptr;
b = nullptr;
c = nullptr;
getA();
getA2();
getA() = nullptr;
std::printf("returning...\n");
}
這樣你就可以釋放之前由unique_ptr
管理的對象,但如果你不手動將它們設置為nullptr
,它們將在退出時自動釋放。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.