没有学过代码编译的原理,以前也没有兴趣去学编译器的相关原理,但是近期通过阅读google开源项目gtest,对我稍有触动。
代码:
main
test示例
TEST宏定义
#define TEST(test_case_name, test_name)
GTEST_TEST_(test_case_name, test_name,
::testing::Test, ::testing::internal::GetTestTypeId())
GTEST_TEST_宏定义:
#define GTEST_TEST_(test_case_name, test_name, parent_class, parent_id)
class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) : public parent_class {
public:
GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {}
private:
virtual void TestBody();
static ::testing::TestInfo* const test_info_;
GTEST_DISALLOW_COPY_AND_ASSIGN_(
GTEST_TEST_CLASS_NAME_(test_case_name, test_name));
};
::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_case_name, test_name)
::test_info_ =
::testing::internal::MakeAndRegisterTestInfo(
#test_case_name, #test_name, "", "",
(parent_id),
parent_class::SetUpTestCase,
parent_class::TearDownTestCase,
new ::testing::internal::TestFactoryImpl<
GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>);
void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody()
google通过一系列的宏和模板类实现了不同test的自动注册。run时一个一个调用test的run就行了。那么google是怎么实现调用前自动注册的呢?
设置两个断点:断点一、main;断点二、MakeAndRegisterTestInfo
运行顺序:
先运行到断点二
再运行到断点一
网上找到的解释:
对于全局对象(global object),VC下是先定义先初始化,但C++标准没做规定。全局对象默认是静态的,全局静态(static)对象必须在main()函数前已经被构造,告知编译器将变量存储在程序的静态存储区,由C++ 编译器startup代码实现。startup代码是更早于程序进入点(main 或WinMain)执行起来的代码,它能做些像函数库初始化、进程信息设立、I/O stream产生等等动作,以及对static对象的初始化动作(也就是调用其构造函数);在main()函数结束后调用它的析构函数。
C/C++程序的启动代码通常包含以下行为,并且按照所列的次序执行:
1、禁止所有中断。
2、从ROM 里复制所有初始化数据到RAM 里。
3、把未初始化数据区清零。
4、未堆栈分配空间并初始化。
5、初始化处理器堆栈指针。
6、创建并初始化堆。
7、(只对C++有效)对所有全局变量执行构造函数和初始化函数。
8、允许中断。
9、调用main。
注意点:
全局变量的初始化顺序是不确定的。
编译器不能定义不同编译单元全局变量的初始化顺序。
在同一个cpp文件里,编译器会保证按照他们出现的顺序初始化。但是如果两个全局变量在不同的编译单元,编译器不能控制他们的构造顺序,我们需要想办法确定他们的构造顺序。本人曾经因为这个问题导致过程序崩溃,所以记忆尤为深刻,在此特意提一下。
关于这一块的讨论,可以参考博文:http://blog.chinaunix.net/uid-26611383-id-3898563.html