class的static const成员变量类内初始化

//***** class.h *****//
class Widget{

public:
    static const int i = 111;
};

//****main.cpp****//
#include <iostream>
#include "class.h"

int main() {

    int bigger = std::max(Widget::i, 1000); //写法1
    std::cout << bigger << std::endl;
}

g++ main.cpp 会出现编译错误:

/tmp/ccuZcfWr.o: In function `main':
main.cpp:(.text+0x17): undefined reference to `Widget::i'
collect2: error: ld returned 1 exit status

这是因为编译器在处理上面的这种写法的static const i变量时, 编译器会直接尝试在i的引用处,用初始化值(111)把i替换掉。在我们上面的这种写法中,std::max的形参是引用类型,本质上是指针,导致该处无法替换,直接就保持原样。但是在链接阶段会报上面的错误。解决方法是重新声明一个中间变量:

//***main.cpp***//
#include <iostream>
#include "class.h"

int main() {

    int i = Widget::i; //此处经过编译器处理之后,就变为了 int i = 111;
    int bigger = std::max(i, 1000);  // 写法2
    std::cout << bigger << std::endl;
}

这样的话程序就可以编译链接通过,输出1000.


注意写法1,这种写法中对于class Widget,我们甚至都没有instance一个对象出来,就使用了i的值;
并且,你可以使用nm命令看下a.out下面,甚至都没有i所对应的符号,具体原因就是我们上面讲的那样,编译器其实是把i给优化掉了的。
思考一下,这种在class内直接初始化既然这么不好用,但它仍然存在,肯定是有他的用处的,那就是,加入你想定义一个数组,数组的大小就是i,那你可以在class.h直接这么写:char ch[i];
因为,最终编译器会为我们处理成 char ch[111];的形式


另外,或许你会想像处理普通的static变量一样,这样:

//***main.cpp***//
#include <iostream>
#include "class.h"

const int Widget::i = 222; //标记1
int main() {

    int bigger = std::max(Widget::i, 1000);  //写法3
    std::cout << bigger << std::endl;
}

我测试过之后发现有编译error:

main.cpp:4:19: error: duplicate initialization of 'Widget::i'
 const int Widget::i = 222;

此时的解决办法就是将 ‘标记1’处的222的初始值去掉,即const int Widget::i; 就可以了,《effective C++》P14专门强调了这一点。


当然,最最标准的写法应该是下面这样:

//****class.h***//
class Widget{

public:
    static const int i; //此处不进行初始化
    //char ch[i]; //会报编译错误:error: array bound is not an integer constant before ']' token
};

//***main.cpp***//
#include <iostream>
#include "class.h"

const int Widget::i = 222;

int main() {

    int bigger = std::max(Widget::i, 1000); //此时这样使用就没问题了,因为此时实实在在存在i这个符号.
    std::cout << bigger << std::endl;
}

这种写法会切切实实的产生i这个符号,那么你在cpp文件里,想怎么使用就怎么使用了。但是有一个需要注意的问题是:在class.h文件里,你如果想定义一个数组char ch[i]; 是不可以的。会报错:

class.h:6:14: error: array bound is not an integer constant before ']' token
     char ch[i];

因为根据一个class的定义,编译器要能计算出intance该class需要多少的空间,因此编译器对于class内的数组的定义要求i应该constant value。而这种写法,单纯的扫描class.h中class的定义并不能计算出要为Widget instance的对象分配多少空间。


突然想到,在c++中,是可以定义所谓的变长数组的,即数组的大小可以为变量。这种变长数组应该猜测是不支持定义在class中。我不熟悉变长数组的定义,查阅资料后,写了一篇关于变长数组的博客,参考这里

所谓的变长数组是有很多的限制的,详细的说明我没找到,但是有两点需要注意:
①可以在函数内部定义变长数据,因为函数内部定义的变长数据是分配在栈上的,栈是运行期间来分配的。
②不可以直接在全局作用域定义一个全局变长数据,编译器会直接报错,因为全局变长数组是要在编译期间确定数组所占的空间的大小。
例如:

int j = 2;
int test[j];

int main() {
	。。。
}

会直接报错:
error: array bound is not an integer constant before ‘]’ token


  1. class的const成员变量,在class内声明,在构造函数初始化列表中初始化;
  2. class的static成员变量,在class内声明(ISO C++ forbids in-class initialization of non-const static member),在cpp文件中初始化。
class A
{
    static int d = 1; //error
};
int A::d = 1 //correct,一般来说:初始化语句会放在cpp文件,类定义放在h文件
  1. class的static const见本博客的前面部分。
  2. 在C++11之后,允许直接在类内初始化值(前提:这个值必须是常量表达式)。并且类内部初始化先于构造函数初始化进行,构造函数初始化会覆盖类内部初始化。参考这里
    所以在C++11之后,总结起来就是: non-static, static const 可以在类内初始化,只有pure static 不能在类内初始化,见2.

ps:以上测试,使用的是 g++ (GCC) 5.2.0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值