我读到在源文件中定义静态数据成员的原因是因为如果它们在头文件中并且多个源文件包含头文件 - 定义将多次输出。我可以看到为什么这对于静态 const 数据成员来说是个问题,但是为什么这对于静态数据成员来说是个问题呢?
如果定义写在头文件中,我不太确定我是否完全理解为什么会出现问题......
变量的多重定义问题是由于语言定义中的两个主要缺陷。
如下所示,您可以轻松解决它。没有直接支持没有技术原因。这与委员会成员选择将其作为优先事项的功能没有足够高的需求有关。
首先,为什么多重定义通常是一个问题。由于 C++ 缺乏对单独编译模块的支持(缺陷 #1),程序员必须通过使用文本预处理等来模拟该功能。然后很容易无意中引入两个或多个同名定义,这很可能是错误的。
对于函数,这是通过inline
关键字和属性解决的。独立函数只能是显式inline
的,而成员函数可以inline
通过在类定义中定义而隐式地定义。无论哪种方式,如果一个函数是,inline
那么它可以在多个翻译单元中定义,并且必须在使用它的每个翻译单元中定义,并且这些定义必须是等价的。
主要是该解决方案允许在头文件中定义类。
不需要这样的语言特性来支持数据,头文件中定义的变量,所以它不存在:你不能有inline
变量。这是语言缺陷#2。
但是,您可以通过对类模板的数据成员的特殊豁免来获得变量的效果。豁免的原因是类模板通常必须在头文件中完全定义(除非模板仅在翻译单元内部使用),因此对于能够具有数据成员的类模板,有必要使用一般规则的豁免或某些特殊支持。委员会选择了豁免规则路线。inline
static
static
template< class Dummy >
struct math_
{
static double const pi;
};
template< class Dummy >
double const math_<Dummy>::pi = 3.14;
typedef math_<void> math;
以上被称为模板化的 const 技巧。据我所知,我曾经是在 [comp.lang.c++] Usenet 组中介绍它的人,所以我不能把功劳归功于其他人。我也在 SO 上发布了几次。
无论如何,这意味着每个 C++ 编译器和链接器都在内部支持并且必须支持inline
数据所需的机制,但该语言没有该功能。
但是,另一方面,C++11 有constexpr
,您可以将上面的内容写成
struct math
{
static double constexpr pi = 3.14;
};
嗯,有一个区别,你不能获取 C++11 的地址math::pi
,但这是一个非常小的限制。
我认为您混淆了两件事:static
数据成员和全局变量标记为static
.
后者有内部链接,这意味着如果你把它们的定义放在一个包含多个翻译单元的头文件中#include
,每个翻译单元都会收到这些变量的私有副本。
默认情况下,标记为具有内部链接的全局变量const
,因此您无需static
明确指定这些变量。因此,链接器不会抱怨全局变量或标记为const
的全局非变量的多个定义,而在其他情况下会抱怨(因为这些变量将具有外部链接)。const
static
关于static
数据成员,这就是 C++11 标准的第 9.4.2/5 段所说的:
static
命名空间范围内的类的数据成员具有外部链接(3.5)。本地类不应有static
数据成员。
这意味着,如果您将它们的定义放在由多个翻译单元组成的头文件d 中,那么无论它们的-qualification 是什么#include
,您最终都会在相应的目标文件中得到同一符号的多个定义(就像非全局变量一样) . 在这种情况下,您的程序将违反One Definition Rule。const
const
另外,这个关于 StackOverflow 的 Q&A可能会让你对这个主题有更清晰的理解。