在读书的时候用PASCAL,那时对计算机算法设计非常的热爱。曾经一度认为任何编程语言都只是实现功能的一种辅助工具。只要对算法有精深的了解,加上对编程经验的积累,总能设计出好的解决方案。用一句流行的电视剧台词就是——手中无剑心中有剑。
后来上大学以后,开始使用C++来编程。C++要比我所了解的PASCAL强大很多,以至于经常会因为一些语法上的细节折腾半天或是写出一些自己都没有把握的代码出来。期间,自己那种“放荡不羁”的编程习惯也慢慢的改掉了,开始注重起一些编程语言本身的技术细节来。即使现在工作了,也会时常关注一些语言本的问题。扯远了,我是想说虽然不需要对你使用的编程语言100%的精通,但是必须了解的基础知识以及一些重要的实现机制还是得掌握清楚。不然在代码的运行中,完全有可能有自己意料之外的结果出现。
这是我前几天看到的一篇帖子,说的是一个静态成员变量初始化的问题。贴主因为对代码输出的结果存在疑惑而发帖来让大家解惑。跟帖的很多人也说不清出问什么会产生这样的结果。还有一些人虽然做出了一些解释,但大多都是自己主观这样认为的,没有什么依据。后来我把同样的代码给同事看,同事也说不清楚具体的缘由。所以我觉得可以把内容贴出来给大家看看,也说下自己的一些理解。
1. 一个例子引出的问题
#include <iostream> using namespace std; class ClassA { public: ClassA() { cout << "ClassA Constructor!" << endl; } void Func() { cout << "ClassA Func() Called! Num:" << m_nNum << endl; } int m_nNum; }; class ClassB { public: ClassB() { cout << "ClassB Constructor! " << endl; s_A.Func(); } protected: static ClassA s_A; }; ClassB TestClass; ClassA ClassB::s_A; int main(int argc, char *argv[]) { system( "PAUSE "); return 0; }
这段代码很简单,声明了2个类: ClassA,ClassB。 在ClassB中定义了一个ClassA的静态成员s_A。接着定义了一个全局ClassB的对象TestClass,之后才去初始化ClassB中ClassA的静态成员s_A。
看到这里就会发现有意思的地方,在初始化其内的静态成员之前就先定义了这个对象,而且在构造函数中回去调用这个未被初始化的静态成员的方法。
很多人会怀疑这段代码多半是不能通过编译的,即使通过了编译运行时也会报错,因为调用的静态成员方法中还涉及到了它的成员变量(这都还没初始化)。
好吧,你错了!这段代码会通过编译并正确执行。而且输出的结果也和大多数人想象的不太一样:
ClassB Constructor!
ClassA Func() Called! Num: 0
ClassA Constructor!
嗯,是的。ClassA是在ClassB之后初始化的。
好吧,如果你觉得有点糊涂或者不解了,可能要找1,2本C++的基础书看看了。这里面可能涉及到让人费解的可能就这几个问题:
- 编译为什么能通过?
- 运行的时候怎么没报错?
- 初始化顺序为什么很奇怪?
(未完)