在《Effective C++(第三版)》条款2中,作者建议“使用编译器替代预处理器”。其中,在替代#define时的第二方案中提到了类内静态成员变量的使用方法。
一般情况下,C++要求变量的使用必须遵循先定义后使用,对于仅仅被声明而从未被定义的变量,编译器不予通过。但是这种情况在C++类内静态变量的定义中存在特殊情况,也就是只声明、不定义、即使用。
1、对于一般的类内专属常量,遵循类内声明,类外定义。该类内专属常量最好使用static const
修饰,对于不需要修改指向对象的指针也最好定义成static const type* const
的形式。
class Test{
public:
int value;
static const int num;//声明int
static const int* const p;//声明int*
Test(int x):value(x){
printf("[%d]\n",Test::num);
}
};
const int Test::num = 3;//定义int
const int* const p = nullptr;//定义int*
//编译正常
2、当class的专属常量是static
修饰且是整数类型(integral type 例如int char bool) 时,编译器允许声明后直接使用而无需再写定义。
class Test{
public:
int value;
static const int num = 3;//仅声明int
Test(int x):value(x){
printf("[%d]\n",Test::num);
}//声明后直接使用
};
3、上述第2条规则中描述的特殊情况中也存在不被允许的情景。如果你确实按照规则2中定义,但你在后续操作中对该静态变量取地址或者你的编译器执意要求你必须给出定义,此时你就不得不给出定义,否则编译出错。
class Test{
public:
int value;
Test(int x):value(x){};
static const int num = 3;//声明
void print(){
//对静态成员进行取地址操作
const int* p = &Test::num;
cout << value << endl;
}
};
const int Test::num;//定义:不定义则编译报错
int main(){
Test i = 1;
i.print();//发生取地址操作
}
上述程序执行了取地址操作,所以给出了定义语句。如果没有定义语句,编译期间就会发生报错,编译器给出undefined reference to `Test::num'
的错误。我们建议请不要在头文件中写入定义语句,而是尽量把它放在一个实现文件中。
4、值得提出是:在初始化时需要区分静态变量是否为整数类型。不同的类型决定了初始化位置不同。
class Test{
public:
Test(){};
static const string str;//声明string
static const int num = 3;//声明int
void print(){
cout << Test::str << endl;
cout << Test::num << endl;
}
};
//非整数数据在定义时赋值
const string Test::str = "sdg";
//int在声明时已经赋值无需在定义时赋值
const int Test::num;
int main(){
Test i;
i.print();
}
整数类型的Test::num
可以在定义或声明中的某一处完成初始化,但不能同时在两处进行初始化,即便初始化的值是相同的也不行。
非整数类型的Test::str
则必须且只能在定义语句中进行初始化,类内的声明语句中不允许初始化操作。