今天又忘了对类内静态成员变量进行初始化了, ,最主要是发生了一奇怪的错误,这里记录一下。
如下代码
//文件B.h class B{ int k ; }; //文件A.h class B; //前置声明 class A{ public: static void Init(){ a = 0; b = new B(); }; public: static int a; //声明但未定义,还未分配内存 static B *b; //声明但未定义,还未分配内存 }; //文件A.cpp #include "B.h" int A::a = 1; B* A::b; //文件C.cpp #include "A.h" void C::Init(){ A::Init = 0; } void C::Set(){ A::a = 1; }
在描述问题前,我们先来了解几个知识点:
- 如果一个类函数未有任何调用,不管他虚函数、纯虚函数、静态函数或者普通的函数是不会出现链接错误的(如果没有编译错误,比如语法错误等);
- 当类实例化时,纯虚函数会在编译阶段报错,虚函数会在链接时报错;
- 一般函数和静态函数可以实例化,但是调用未实现的函数会出现链接错误;
- 类静态成员变量初始化在类体外进行,而前面不加static,以免与一般静态变量或对象相混淆。
- 初始化时不加该成员的访问权限控制符private,public等。
- 初始化时使用作用域运算符来标明它所属类,因此,静态数据成员是类的成员,而不是对象的成员。
这个时候在回头看代码,我们会发现在A.h文件中,A类有静态变量a和*b,这个时候只是声明,使用前置声明class B;即使我们不知道任何与B相关的信息(但必须存在,否则会报编译错误)同样可以编译通过。这个时候并没有给a,*b分配空间。所以如果要使用a或者*b就必须得在A.cpp进行定义(申请空间,可以进行初始化)。
如果这个时候我们A.cpp的对a和*b的定义,并注释C.cpp的两个函数,我们编译链接没有发现任何错误。但是如果我们在C.cpp中使用了Init()函数,它会调用A类的Init(),这个时候链接阶段就会报错未定义 undefined reference,这一次我不小心就是这样,在A.cpp中未对静态成员变量定义并初始化,且直接在C.cpp开始使用,诡异的事情发生了,报的错误还是链接的undefined reference,但是奇怪的是编译器提示错误的位置在另一个文件里面(被C.cpp包含),那个文件未对A类没有任何访问。C.cpp屏蔽对该文件的包含后发现只对赋值类型的语句报错,如A::a=1;但是if(1 == A::a)也不会报错的。当时看到这里很懵逼,还想着if(1 == A::a)都没有报错未定义,说明定义了的呀,但是赋值就是不行。以前都是写好A.cpp对A::Init()调用后,很容易就会发现这个未定义的链接错误,这次报错的提示确是诡异,目前还不知道为啥会在报错中提示在另外的文件。