• 欢迎访问我的博客,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站
  • 本网站关闭了评论功能,联系请点击→邮箱
  • Ctrl+D 可快捷收藏本站点

关于类内静态成员变量的初始化及其编译异常

C/C++ gql 4年前 (2021-03-29) 1169次浏览

今天又忘了对类内静态成员变量进行初始化了, :roll: ,最主要是发生了一奇怪的错误,这里记录一下。
如下代码

//文件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()调用后,很容易就会发现这个未定义的链接错误,这次报错的提示确是诡异,目前还不知道为啥会在报错中提示在另外的文件。


如未注明 , 均为原创。转载请注明原文链接:关于类内静态成员变量的初始化及其编译异常
喜欢 (0)