16. 类的 static const 成员
16.1. const 成员
const 数据成员的初始化只能在类的构造函数的初始化列表中进行。声明 const 变量时不能初始化。
Note
必须使用初始化列表的情形:
常量成员 ,因为常量只能初始化不能赋值,所以必须放在初始化列表里面。
引用类型 ,引用必须在定义的时候初始化,并且不能重新赋值,所以也要写在初始化列表里面。
没有默认构造函数的 类类型 ,因为使用初始化列表不调用默认构造函数来初始化,而是直接调用拷贝构造函数初始化。
Warning
成员是按照他们在类中 声明 的顺序进行初始化的,而不是按照他们在初始化列表出现的顺序初始化的。
16.2. static 成员
不能在定义对象时对静态变量进行 定义和初始化 ,即不能用构造函数进行初始化。 初始化在类体外进行,前面不加 static 修饰。
16.3. static const 成员
静态常量成员,可以直接初始化(static const 和 const static 含义相同)。
1/* header.h */
2class Solution
3{
4public:
5 static void print()
6 {
7 cout << var << endl; // 100
8 cout << (mapping.begin()->second)[0] << endl; // 'a'
9 }
10
11private:
12 static const map<char, vector<char>> mapping; // 常量声明式
13 static const int var = 100; // 常量声明式(直接初始化)
14};
15
16/* source.cpp */
17const map<char, vector<char> > Solution::mapping = {{'2', {'a', 'b', 'c'}},
18 {'3', {'d', 'e', 'f'}},
19 {'4', {'g', 'h', 'i'}}}; // mapping 的定义
20
21// 注:const map 只能通过成员函数 at 或迭代器 const_iterator 访问元素(it->first, it->second),不能通过下标[]的方式访问。
22
23// 对应类的 static const int/char/bool 成员常量,如果不取他们的地址,则可以直接声明并使用,而无需提供以下的定义式。
24const int Solution::var; // var 的定义。由于常量 var 在类内声明时已经获得了初始值,因此定义时不可以再设初始值。
Note
初始化 :变量还没有值,现在赋予它一个值。
赋值 :变量已经有一个值,现在擦除它之前的值,赋予一个新的值。
声明 :告诉编译器类型及其细节,不涉及内存分配。
定义 :编译器将在内存地址上为该对象定址,即分配内存;定义的同时也作了声明。
Warning
static 和 const 不能 同时 修饰成员函数,只能修饰成员变量。 因为常量成员函数(const)拥有一个 this 指针,是一个指向常量类类型的常量指针,而静态成员函数(static)没有 this 指针。
Note
- 头文件中应该写什么?
变量和函数的声明,而不是定义。如:
extern int a; void f();
是允许的,而int a; void f() {};
是不允许的。const 全局变量可以定义在头文件中,不会因为头文件重复包含而导致变量重复定义的编译错误。 但是在定义指针时要注意,
const char* p = "name"
定义的指针不是 const,可能导致错误;而char* const p = "name"
不会。内联函数。
类的定义。成员函数定义在类的定义体内,编译器会视其为内联函数;如果定义在类的头文件内,而没有写进类定义体内,是不合法的,需要定义 在别的源文件(.cpp)文件内。
函数模板和类模板成员函数。
把声明在头文件(header.h)中的函数或类成员函数定义在一个源文件(source1.cpp)中,需要包含该头文件(#include “header.h”),当 另一个源文件(source2.cpp)需要调用上述函数时,只需要包含头文件(#include “header.h”),而不是包含函数定义的源文件(source1.cpp)。
16.4. 参考资料
C++ static、const和static const类型成员变量声明以及初始化
头文件中定义 const 全局变量应注意的问题
头文件重复包含和变量重复定义
C++ 初始化列表
C++的一大误区——深入解释直接初始化与复制初始化的区别
C++构造函数初始化列表与赋值