破坏全局和静态对象的最佳方法



用静态存储持续时间结束对象生命周期的最佳方法是什么?

当前实现找到__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 %sn", __PRETTY_FUNCTION__, mName.c_str());
}
~A()
{
std::printf("%s %sn", __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 = &current->fns[i];
if (4 == f->type && pTarget == f->arg)
{
void (*destruct) (void *arg, int status) = f->fn;
asm ("ror $2*8+1, %0nxor %%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

静态对象将随着程序的终止而被销毁。

如果你想管理资源,不要让它成为静态的或使用静态指针。在这里,您可以分配和取消分配相应的资源。这种方法非常接近于singleton,它被认为是一种反模式。

结论:如果你需要管理一个资源,不要让它成为静态的。

需要以这种方式处理生命周期的默认行为表明您的应用程序中存在设计缺陷。

因此,您应该考虑重组您的程序,使其不使用全局变量。或者至少改变处理全局变量的方式。因此,如果你真的需要全局变量并提前发布,那么切换到unique_ptr:

#include <iostream>
#include <functional>
#include <memory>
struct A
{
A(std::string pName)
: mName(pName)
{
std::printf("%s %sn", __PRETTY_FUNCTION__, mName.c_str());
}
~A()
{
std::printf("%s %sn", __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,它们将在退出时自动释放。

相关内容

  • 没有找到相关文章

最新更新