什么是静态成员的未定义引用?

20
我刚写了一个带有一些静态数据成员的类,但现在我收到了关于“未定义引用”的错误。为什么会这样?我做错了什么吗?

http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.12 - PlasmaHH
2个回答

30
为了理解这个,你应该对编译和链接以及声明和定义之间的区别有很好的理解。
考虑以下类:
//In header file
class Example {
    static bool exampleStaticMember;
};

在这里,声明了exampleStaticMember但未定义。这意味着,如果以某种方式使用exampleStaticMember,使其必须具有地址,则必须有单独的定义。一般来说,在类定义中静态数据成员的任何声明都不是该成员的定义。
所需的声明通常放在包含类成员其他定义的cpp文件中。它必须与类定义在相同的名称空间中。定义通常如下所示:
//In source file:
//This may optionally have an initialiser (eg "= true")
bool Example::exampleStaticMember; 

在任何cpp文件中都可以定义该变量,但不应将其放在类的头文件中,因为这很可能会违反One Definition Rule
特殊情况下,如果静态成员变量是常量整数或枚举类型,则可以在类定义中对其进行初始化。
//In header file
class Example {
    static const int initialised = 15;
};

在这种情况下,cpp文件中的定义仍然是必需的,但不允许有初始化器:
//In source file
//Note: no initialiser!
const int Example::initialised;

这样初始化的静态成员可以在常量表达式中使用。

模板

对于模板的静态数据成员,情况略有不同。静态成员应该与类的其余部分一起在头文件中定义:

//In header file
template<typename T>
class Example {
    static int exampleInt;
    static T exampleT;
}
template<typename T> int Example<T>::exampleInt;
template<typename T> T Example<T>::exampleT;

这是因为类模板的静态数据成员有一个特定的例外,可以避免“一次定义规则”。 static 的其他用途 当应用于不在类范围内的函数和对象时, static 关键字可能具有非常不同的含义。
当应用于函数作用域中的对象时,它声明了一个对象,在函数的第一次执行中初始化,并在函数调用之间保持其值。
当应用于命名空间范围内的对象或函数时(在任何类或函数定义之外),它声明具有 内部链接 的对象或函数。对于对象,此用法已被弃用,因为 未命名名称空间 提供了更好的替代方案。

作为特例,如果静态成员变量是const整数或枚举类型,则可以在类定义中具有初始化器。这里指的是类的定义吗? - Sam Goldberg
@SamGoldberg:不,如果我说“声明”,那么我将指的是前向声明(例如class Example;)。通过“定义”,我指的是声明类成员的代码。(是的,这些成员本身可以在类定义之外定义,但这在这里并不重要)。 - Mankarse

5

在.cpp文件中,你需要实例化在头文件中定义的静态成员。例如:

// foo.h

class foo {
    static int X;
};


// foo.cpp

#include "foo.h"

int foo::X = 0;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接