在头文件或.cpp文件中声明C++类变量?

6

到目前为止,我一直是按照以下方式使用类:

GameEngine.h将类声明如下:

class GameEngine {
public:
    // Declaration of constructor and public methods

private:
    InputManager inputManager;
    int a, b, c;

    // Declaration of private methods
};

我的GameEngine.cpp文件仅仅实现了这些方法。
#include "____.h"    
GameEngine::GameEngine() {

}

void GameEngine::run() {
    // stuff
}

然而,我最近读到变量声明不应该在头文件中。在上面的例子中,这将是一个inputManager和a、b、c。 现在,我一直在寻找变量声明的位置,最接近的答案是这个:Variable declaration in a header file 然而,我不确定extern的使用是否有意义;我只是声明私有变量,它们只会在类实例本身中使用。我的变量声明在头文件中是否正确?或者我应该把它们放在其他地方?如果我应该把它们放在cpp文件中,它们是否直接放在#include下面?

7
“变量声明不应该在头文件中”……什么?成员变量必须与类声明一起声明。 - Cory Kramer
如果变量要在全局范围内使用,那么 extern 放在头文件中,并且你需要在某个 .cpp 文件中定义变量 一次。如果它们是内部使用的全局变量,则放置在 .cpp 文件中。如果它们在逻辑上属于类的一部分,则将它们作为成员变量。 - user3920237
变量声明和成员变量声明是不同的。此外,非成员变量的定义会给你带来问题,而不是声明。 - Some programmer dude
2
你想使用Pimpl习惯用法吗?我不明白你所阅读的内容是什么。 - πάντα ῥεῖ
尝试过但无法再找到原始语句了。实际上,它在SO的某个地方,但自从我尝试寻找答案以来,我的历史记录已经太满了,无法再次定位它。对我来说,这听起来像是一个普遍的声明,即在.h文件中声明变量是不好的编程风格;然而,考虑到你们的反应,我可能误解了它。 - Kefir
3个回答

8
不要将类型的成员与变量混淆。类/结构体定义仅描述了构成一种类型的内容,而没有声明任何变量的存在,也没有在内存上构造任何东西或可寻址的东西。
传统意义上,现代类设计实践建议您将它们视为“黑匣子”:输入一些信息,它们可以执行某些任务,可能输出其他一些信息。我们经常使用类方法来做到这一点,简要描述其.h/.hpp/.hxx文件中的签名,并在.cpp/.cc/.cxx文件中隐藏实现细节。
虽然相同的哲学可以应用于成员,但当前的C++状态以及如何单独编译翻译单元使这种方式更难实现。这里肯定没有任何“开箱即用”的帮助。基本的、根本性的问题是,几乎任何使用您的类的东西都需要知道以字节为单位的大小,这是由成员字段和声明顺序所限制的。即使它们是私有的,任何类型的范围之外的东西都不应该能够操纵它们,但它们仍然需要简要地知道它们是什么。
如果您真的想向外部隐藏此信息,某些习惯用法,如PImpl和内联PImpl,可以帮助。但我建议您不要走这条路,除非您实际上需要:
1.编写具有半稳定ABI的库,即使您进行了大量更改。
2.需要隐藏不可移植的平台特定代码。
3.由于包含文件过多而需要减少预处理器时间。
4.需要减少由此信息曝光直接影响的编译时间。
指南实际上是在谈论永远不要在头文件中声明全局变量。任何间接地利用您的头文件的翻译单元都将像头文件指令一样声明自己的全局变量。每个东西在单独检查时都会编译得很好,但连接器会抱怨您对同一物体进行了多次定义(这在C++中是一个大忌)。如果您需要保留内存/构造某些内容并将其绑定到变量名称,请始终尝试在源文件中完成。

我这里有一个后续问题:Inputmanager inputManager; 这一行只是声明吗?还是它会尝试预留内存?我最初想在GameEngine的构造函数中初始化inputManager,但是它无法编译,因为InputManager没有默认构造函数。(没有默认构造函数,但我也没有在任何地方调用不带参数的构造函数。)这让我感到惊讶,因为我只想声明变量的存在,但似乎需要在声明时立即使用默认构造函数。 - Kefir
根据您的类声明,您正在声明它完全包含一个InputManager,但是实际上还没有构建任何内容。一旦您通过构造函数在运行时创建了一个GameEngine对象,它将有一个完全专用于存储整个InputManager的字节部分。构造函数还将尝试构建一个InputManager。(如果您没有明确说明如何构建一个InputManager,并且没有默认构造函数,则可以理解为编译期间会出现错误)在C++中,“所有权”是一个太广泛的主题,无法在评论中讨论。 - ZaldronGG

5

类成员变量必须在类定义中声明,通常在头文件中进行。这应该完全正常地完成,没有任何extern关键字,就像你迄今为止一直在做的那样。

只有不是类成员且需要在头文件中声明的变量才应该声明为extern。


3
作为一般规则:
在同一类中的多个功能中使用的变量应放在类声明中。
用于单个函数的临时变量应放在函数本身中。
看起来 "InputManager inputManager;" 应该放在类头文件中。
从这里很难知道 "int a, b, c;" 的用途。它们似乎是临时变量,最好放在它们被使用的函数中,但如果没有适当的上下文,我无法确定。
"extern" 在这里没有用处。

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