没有学过代码编译的原理,以前也没有兴趣去学编译器的相关原理,但是近期通过阅读google开源项目gtest,对我稍有触动。

代码:

main

image

test示例

image

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

运行顺序:

先运行到断点二

image

再运行到断点一

image

网上找到的解释:

对于全局对象(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

posted on 2014-10-28 16:14  逸蒙  阅读(370)  评论(0编辑  收藏  举报