C++ 我如何安排一些代码运行'_atexit()';功能已完成
我正在编写一个内存跟踪系统,我实际遇到的唯一问题是,当应用程序退出时,任何没有在构造函数中分配但在解构器中释放的静态/全局类都会在我的内存跟踪工具将分配的数据报告为泄漏后释放 据我所知,正确解决这一问题的唯一方法是强制将内存跟踪器的_atexit回调放在堆栈的头部(这样它就被称为最后一个回调),或者在整个_atexit堆栈解卷后执行它。实际上是否有可能实现这两种解决方案中的任何一种,或者是否存在我忽略的另一种解决方案 编辑:C++ 我如何安排一些代码运行'_atexit()';功能已完成,c++,allocation,atexit,C++,Allocation,Atexit,我正在编写一个内存跟踪系统,我实际遇到的唯一问题是,当应用程序退出时,任何没有在构造函数中分配但在解构器中释放的静态/全局类都会在我的内存跟踪工具将分配的数据报告为泄漏后释放 据我所知,正确解决这一问题的唯一方法是强制将内存跟踪器的_atexit回调放在堆栈的头部(这样它就被称为最后一个回调),或者在整个_atexit堆栈解卷后执行它。实际上是否有可能实现这两种解决方案中的任何一种,或者是否存在我忽略的另一种解决方案 编辑: 我正在为Windows XP工作/开发,并使用VS2005进行编译。我
我正在为Windows XP工作/开发,并使用VS2005进行编译。我已经读过多次,您无法保证全局变量的构造顺序()。我认为从这一点可以很安全地推断析构函数的执行顺序也不能保证 因此,如果您的内存跟踪对象是全局的,您几乎肯定无法保证您的内存跟踪对象将在最后(或先构造)被销毁。如果它最后没有被破坏,并且其他分配未完成,那么它会注意到您提到的泄漏
另外,这个_-atexit函数是为哪个平台定义的?最后执行内存跟踪器的清理是最好的解决方案。我发现最简单的方法是显式地控制所有相关全局变量的初始化顺序。(有些库将其全局状态隐藏在奇特的类或其他类型中,认为它们遵循的是一种模式,但它们所做的只是阻止这种灵活性。) 示例main.cpp:
#include "global_init.inc"
int main() {
// do very little work; all initialization, main-specific stuff
// then call your application's mainloop
}
其中全局初始化文件包含对象定义,而#包含类似的非头文件。按您希望的顺序排列此文件中的对象,它们将按相反的顺序被销毁。C++03中的18.3/8保证销毁顺序反映了构造:“具有静态存储持续时间的非本地对象按与其构造函数完成相反的顺序销毁。”(该部分讨论的是exit()
,但从main返回的顺序相同,请参见3.6.1/5。)
作为奖励,您可以保证在进入main之前初始化所有全局变量(在该文件中)。(标准中没有保证,但如果实现选择允许。)atexit由C/C++运行时(CRT)处理。它在main()返回后运行。也许最好的办法是用你自己的CRT代替标准的CRT 在Windows上,tlibc可能是一个很好的起点: 查看mainCRTStartup的代码示例,并在调用_doexit()后运行代码; 但在出口程序之前 或者,您可以在调用ExitProcess时得到通知。调用ExitProcess时,会发生以下情况(根据):
显然,这一切都是相当粗糙的,请小心处理。这取决于开发平台。例如,Borland C++有一个可以应用于此的语法。(Borland C++ 5,C 1995) 这两个pragma允许程序指定在程序启动时(在调用主函数之前)或程序退出时(在程序通过_exit终止之前)应调用的函数。 指定的函数名必须是以前声明的函数,如下所示: 可选优先级应在64到255之间,最高优先级为0;默认值为100。具有较高优先级的函数首先在启动时调用,最后在退出时调用。从0到63的优先级由C库使用,用户不应使用。
也许您的C编译器也有类似的功能?我遇到了这个问题,还编写了一个内存跟踪器 有几件事: 除了破坏,你还需要处理建筑。在构建内存跟踪器之前(假设它是作为一个类编写的),要准备好调用malloc/new。所以你需要你的类知道它是被构造还是被破坏了
class MemTracker
{
enum State
{
unconstructed = 0, // must be 0 !!!
constructed,
destructed
};
State state;
MemTracker()
{
if (state == unconstructed)
{
// construct...
state = constructed;
}
}
};
static MemTracker memTracker; // all statics are zero-initted by linker
在每次调用跟踪程序的分配上,构建它
MemTracker::malloc(...)
{
// force call to constructor, which does nothing after first time
new (this) MemTracker();
...
}
奇怪,但却是真的。不管怎样,在毁灭之前:
~MemTracker()
{
OutputLeaks(file);
state = destructed;
}
所以,在销毁时,输出您的结果。但我们知道,还会有更多的电话。怎么办?嗯
MemTracker::free(void * ptr)
{
do_tracking(ptr);
if (state == destructed)
{
// we must getting called late
// so re-output
// Note that this might happen a lot...
OutputLeaks(file); // again!
}
}
最后:
- 小心穿线
- 注意不要在跟踪器中调用malloc/free/new/delete,或者能够检测递归,等等:-)
- 我忘了,如果你把跟踪器放在一个DLL中,你可能需要自己加载library()(或dlopen,等等)来增加引用计数,这样你就不会过早地从内存中删除。因为阿尔特
~MemTracker() { OutputLeaks(file); state = destructed; }
MemTracker::free(void * ptr) { do_tracking(ptr); if (state == destructed) { // we must getting called late // so re-output // Note that this might happen a lot... OutputLeaks(file); // again! } }
#include <iostream> using std::cout; using std::endl; // Typedef for the function pointer typedef void (*_PVFV)(void); // Our various functions/classes that are going to log the application startup/exit struct TestClass { int m_instanceID; TestClass(int instanceID) : m_instanceID(instanceID) { cout << " Creating TestClass: " << m_instanceID << endl; } ~TestClass() {cout << " Destroying TestClass: " << m_instanceID << endl; } }; static int InitInt(const char *ptr) { cout << " Initializing Variable: " << ptr << endl; return 42; } static void LastOnExitFunc() { puts("Called " __FUNCTION__ "();"); } static void CInit() { puts("Called " __FUNCTION__ "();"); atexit(&LastOnExitFunc); } static void CppInit() { puts("Called " __FUNCTION__ "();"); } // our variables to be intialized extern "C" { static int testCVar1 = InitInt("testCVar1"); } static TestClass testClassInstance1(1); static int testCppVar1 = InitInt("testCppVar1"); // Define where our segment names #define SEGMENT_C_INIT ".CRT$XIM" #define SEGMENT_CPP_INIT ".CRT$XCM" // Build our various function tables and insert them into the correct segments. #pragma data_seg(SEGMENT_C_INIT) #pragma data_seg(SEGMENT_CPP_INIT) #pragma data_seg() // Switch back to the default segment // Call create our call function pointer arrays and place them in the segments created above #define SEG_ALLOCATE(SEGMENT) __declspec(allocate(SEGMENT)) SEG_ALLOCATE(SEGMENT_C_INIT) _PVFV c_init_funcs[] = { &CInit }; SEG_ALLOCATE(SEGMENT_CPP_INIT) _PVFV cpp_init_funcs[] = { &CppInit }; // Some more variables just to show that declaration order isn't affecting anything extern "C" { static int testCVar2 = InitInt("testCVar2"); } static TestClass testClassInstance2(2); static int testCppVar2 = InitInt("testCppVar2"); // Main function which prints itself just so we can see where the app actually enters void main() { cout << " Entered Main()!" << endl; }
Called CInit(); Called CppInit(); Initializing Variable: testCVar1 Creating TestClass: 1 Initializing Variable: testCppVar1 Initializing Variable: testCVar2 Creating TestClass: 2 Initializing Variable: testCppVar2 Entered Main()! Destroying TestClass: 2 Destroying TestClass: 1 Called LastOnExitFunc();
extern _CRTALLOC(".CRT$XIA") _PIFV __xi_a[]; extern _CRTALLOC(".CRT$XIZ") _PIFV __xi_z[]; /* C initializers */ extern _CRTALLOC(".CRT$XCA") _PVFV __xc_a[]; extern _CRTALLOC(".CRT$XCZ") _PVFV __xc_z[]; /* C++ initializers */ extern _CRTALLOC(".CRT$XPA") _PVFV __xp_a[]; extern _CRTALLOC(".CRT$XPZ") _PVFV __xp_z[]; /* C pre-terminators */ extern _CRTALLOC(".CRT$XTA") _PVFV __xt_a[]; extern _CRTALLOC(".CRT$XTZ") _PVFV __xt_z[]; /* C terminators */