今天遇到一个问题:(原来没见过这种声明方式,QAQ还是我太孤陋寡闻了)
.h文件中
class CProtocol { typedef std::unordered_map<UINT32, CProtocol*> CProtocolMap; ... static CProtocolMap sm_oPtcMap; }
.cpp文件中
CProtocol::CProtocolMap CProtocol::sm_oPtcMap;
让人有点懵,所以接下来要详细了解一下类内的static变量。
众所周知,将一个类内的某个成员变量声明为static型,可以使得该类实例化得到的对象实现对象间数据共享。在C++中,通常将一个类的声明写在头文件中,将这个类的具体定义(实现)写在cpp源文件中。因此,就引出了static成员变量的声明与定义问题:
1. 如果一个类内成员变量是static的,且需要将之设定为常量(const),那么这个变量声明与初始化均可写在头文件内。
class Scanner { public: const static int MAX_SIZE = 0xFFFF; ... };
这里直接将MAX_SIZE声明与定义写在了头文件。这很好理解,编译器在为这个类分配内存空间的时候,已经知道了这个类内变量无需为每个对象“拷贝”一份(因为它是static的),并且又知道了它在程序运行过程中的值保持不变(因为它是const的),那么就可以直接将其处理。因此头文件内的类声明实现信息已经足够,从而这样的写法是合理的。(在当前的c++版本是支持的,以前好像不行)
需要注意的是,不能在头文件内声明const static成员变量,而在具体源cpp文件内实现其初始化。因为这样编译器需要根据具体的实现文件来确定该成员变量的初始值,若实际应用中没有相应的实现文件(源cpp文件)来对这个成员变量实现初始化,则编译器无法明确意图,从而无法完成编译。
2.如果一个类内成员变量是static的,但不需要将其设定为常量(const),那么这个变量声明于头文件内,初始化(定义/实现)写在对应的cpp源文件中。
// Scanner.hpp class Scanner { public: static int line; ... };
// Scanner.cpp #include "Scanner.hpp" int Scanner::line = 1; ...
这样的实现方式给了类实现者一种相对的自由。这样就可以针对不同的实现文件实现不同的类内static字段初始化。比如有两个Scanner实现文件: Scanner1.cpp 和 Scanner2.cpp,那么这两个源文件分别#include "Scanner.hpp",就可以分别实现各自的类内line值初始化。当然,在实际应用时,切不可同时使用Scanner1和Scanner2,因为这样会发生符号表重定义冲突(因为两个cpp文件均实现了Scanner,符号表内重复填充属性字段)。
需要注意的是,此时不能将上述line的初始化写在头文件(Scanner.hpp)里。道理类似,因为编译器发现这个字段并不是const的,也就是说这个字段可以被不同的实现文件(cpp文件)来具体确定其初始值,那么编译器就不负责在声明阶段对其实现初始化,因此在头文件内初始化一个类内非const的static成员变量是非法的。
还有一个知识,就是对于类内的const和static ,const常量不允许在声明时定义只允许在初始化函数中初始化一次,static静态变量不允许在声明时定义。
转载自:Modnar