11.4.2 MSVC CRT的全局构造和析构(2)
运行这个程序,可以得到如"hello"的输出。为了验证A~Z的这个字母表排列,读者可以修改SECNAME,使之不处于.CRT$XCA和.CRT$XCZ之间,理论上不会得到任何输出。而如果将段名改为.CRT$XCV(V的字典序在U之后),那么foo函数将在main执行之后执行。
MSVC CRT 析构
最后来看看MSVC的全局析构的实现,在MSVC里,只需要在全局变量的定义位置上设置一个断点,就可以看到在.CRT$XC 中定义的全局初始化函数的内容。我们仍然使用本章一开头的HelloWorld来作为示例:
#include <iostream> class HelloWorld { public: HelloWorld() {std::cout << "hi\n";} ~HelloWorld(){std::cout << "bye\n";} }; HelloWorld Hw; int main() { return 0; } |
这里在加粗的位置上设置断点。运行程序并中断之后查看反汇编可以得到初始化函数的内容:
011B1B70 mov eax,dword ptr [__imp_std::cout (11B2054h)] 011B1B75 push offset string "hi\n" (11B2124h) 011B1B7A push eax 011B1B7B call std::operator<<<std::char_traits<char> > (11B1140h) 011B1B80 push offset `dynamic atexit destructor for 'Hw'' (11B1B90h) 011B1B85 call atexit (11B13B0h) 011B1B8A add esp,0Ch 011B1B8D ret
|
在这里可以看见这段程序首先调用了内联之后的HelloWorld的构造函数,然后和g++相同,调用atexit将一个名为dynamic atexit destructor for 'Hw''的函数注册给程序退出时调用。而这个dynamic atexit destructor for 'Hw''函数的定义也能很容易找到:
`dynamic atexit destructor for 'Hw'': 011B1B90 mov eax,dword ptr [__imp_std::cout (11B2054h)] 011B1B95 push offset string "bye\n" (11B2128h) 011B1B9A push eax 011B1B9B call std::operator<<<std::char_traits<char> > (11B1140h) 011B1BA0 add esp,8 011B1BA3 ret |
可以看出,这个函数的作用就是在对象Hw调用内联之后进行析构。看到这里,我想各位读者肯定有跟我一样的心情,那就是希望举一反三的愿望并不是不切实际的,它是实实在在存在的。Glibc下通过__cxa_exit()向exit()函数注册全局析构函数;MSVC CRT也通过atexit()实现全局析构,它们除了函数命名不同之外几乎没有区别。
【责任编辑:
云霞 TEL:(010)68476606】