//***** 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
- class的const成员变量,在class内声明,在构造函数初始化列表中初始化;
- 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文件
- class的static const见本博客的前面部分。
- 在C++11之后,允许直接在类内初始化值(前提:这个值必须是常量表达式)。并且类内部初始化先于构造函数初始化进行,构造函数初始化会覆盖类内部初始化。参考这里
所以在C++11之后,总结起来就是: non-static, static const 可以在类内初始化,只有pure static 不能在类内初始化,见2.
ps:以上测试,使用的是 g++ (GCC) 5.2.0