【问题标题】:C++ Declaration of class variables in header or .cpp?头文件或.cpp中的类变量的C ++声明?
【发布时间】:2015-02-14 16:18:57
【问题描述】:

到目前为止,我一直通过以下方式使用类:

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 下吗?

【问题讨论】:

  • “变量声明不应该在头文件中”....什么?成员变量必须在类声明中声明。
  • 如果该变量打算在全局范围内使用,则 extern 进入标题,并且您在某个 .cpp 文件中定义变量 once。如果它们是内部使用的全局变量,那么它们会进入 .cpp。如果它们在逻辑上是类的一部分,则将它们设为成员变量。
  • 变量声明和member变量声明是不同的。此外,给你带来问题的是非成员变量的定义,而不是声明。
  • 您的意思是使用Pimpl idiom 吗?我不明白你在那里读到的内容。
  • 试过但找不到原来的语句了。实际上,它在 SO 上的某个地方,但是自从我尝试寻找答案后,我的历史太满了,无法再次找到它。对我来说,在 .h 文件中声明变量是一种糟糕的编程风格,这听起来像是一个笼统的说法。但是,考虑到您的反应,我可能误解了它。

标签: c++ header-files variable-declaration


【解决方案1】:

不要将类型的成员与变量混淆。类/结构定义只是描述构成类型的内容,而没有实际声明任何变量的存在,任何要在内存上构造的东西,任何可寻址的东西。

在传统意义上,现代类设计实践建议你假装它们是“黑匣子”:东西进去,它们可以执行某些任务,也许会输出一些其他信息。我们一直使用类方法来做到这一点,在 .h/.hpp/.hxx 文件中简要描述它们的签名,并将实现细节隐藏在 .cpp/.cc/.cxx 文件中。

虽然可以将相同的理念应用于成员,但 C++ 的当前状态,如何单独编译翻译单元使得这种方式更难实现。在这里肯定没有什么“开箱即用”可以帮助您。基本的基本问题是,几乎任何要使用您的类的东西都需要知道以字节为单位的大小,这是受成员字段和声明顺序的约束。即使它们是私有的,并且类型范围之外的任何东西都不能操纵它们,它们仍然需要简单地知道它们是什么。

如果您确实想向外界隐藏这些信息,某些习语(例如 PImpl 和内联 PImpl)可以提供帮助。但我建议你不要这样做,除非你真的是:

  1. 使用半稳定的 ABI 编写库,即使您进行了大量更改。
  2. 需要隐藏不可移植的、特定于平台的代码。
  3. 由于包含丰富的内容,需要减少预处理器时间。
  4. 需要减少这种信息公开直接影响的编译时间。

该指南实际上是在谈论永远不要在标头中声明全局变量。任何利用您的标头的翻译单元,即使是间接的,最终都会根据标头指令声明自己的全局变量。单独检查时,一切都可以正常编译,但链接器会抱怨您对同一事物有多个定义(这在 C++ 中是一个很大的禁忌)

如果您需要保留内存/构造某些内容并将其绑定到变量的名称,请始终尝试在源文件中实现。

【讨论】:

  • 我这里有一个后续问题:Inputmanager inputManager; 是行吗?只是一个声明,还是会尝试保留内存?本来想在GameEngine的构造函数中初始化inputManager,但是因为没有InputManager的默认构造函数,所以编译不了。 (没有默认构造函数,但我也没有在任何地方调用没有参数的构造函数。)这让我感到惊讶,因为我只想声明变量的存在,但似乎它需要在声明时使用默认构造函数.
  • 在您的类声明中,您声明它包含一个完整的 InputManager,但到目前为止还没有实际构建任何东西。只要您通过构造函数在运行时中创建 GameEngine 对象,它就会有一段字节完全专用于在其中存储整个 InputManager。构造函数还将尝试构建一个 InputManager。 (如果您没有明确解释如何构建一个,并且不存在默认构造函数,那么它会在编译期间抱怨它是有道理的)C++ 中的“所有权”是一个过于宽泛的话题,无法评论。
【解决方案2】:

类成员变量必须在类定义中声明,通常在头文件中。这应该在没有任何 extern 关键字的情况下完成,完全正常,就像你到目前为止所做的那样。

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

【讨论】:

    【解决方案3】:

    作为一般规则:

    要与同一类中的许多函数一起使用的变量放在类声明中。

    单个函数的临时变量进入函数本身。

    看来InputManager inputManager;属于类头。

    int a, b, c; 从这里更难知道。它们是用来做什么的?它们看起来像临时变量,在使用它们的函数中会更好,但如果没有适当的上下文,我不能肯定地说。

    extern在这里没用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-11-24
      • 2010-11-12
      • 2020-11-01
      • 1970-01-01
      • 2018-05-22
      • 1970-01-01
      • 2016-12-19
      相关资源
      最近更新 更多