2

当类定义了许多私有常量时,有什么理由不应该在类声明中#include 另一个文件?

我正在编写一个遵循简单状态转换系统的类,并定义一个由多个状态组成的处理计划,每个状态都包含一系列步骤。因为类必须在各种函数中引用这些状态和步骤(例如,当根据当前状态和步骤确定要应用哪个处理时),我最终在类的声明中定义了一堆私有枚举来进行实现可读(所以我可以参考 kStates_ModeTransition 和 kStateSteps_ModeTransition_PrepareNewSetup 等内容,而不仅仅是使用与这些状态和步骤相关的原始整数值)。

随着状态和状态步骤的列表越来越长,枚举的这个集合在类声明的中间变成了相当长的、尴尬的代码块,我觉得这些常量与实现的联系比与接口的联系更多——类的用户不一定要知道它们。有什么理由我不应该将所有这些枚举移动到另一个文件,然后只是将该文件#include 到类声明的私有部分?我还没有遇到过在类的主体中使用#include 似乎合适的另一种情况,所以我想知道是否有更好的方法来处理这个问题,或者有任何特殊原因,这样的#include 是不好的形式。此外,是否有任何合理的标准文件扩展名可用于此类文件,仅用于文本插入(它不是真正的标题......)?只是.txt?

谢谢!

编辑:再看看上面提到的替代方案之一是否完全解决了我的困境:

试图只坚持要领,这是我当前结构的一个例子

// Processor.h

class Processor
{
public:

    Processor();
    void Process( float* input, float* output, int numSamples );

private:

    // List of possible states
    enum
    {
        kStates_Default,
        kStates_SettingChangeImmediate,
        kStates_SettingChangeCrossfade,
        kStates_SpecialProcessing,
        kStates_NumStates
    };

    // Lists of steps for each state...
    enum
    {
        kStateSteps_Default_PrepareInput,
        kStateSteps_Default_CalculateIntermediateValues,
        kStateSteps_Default_CalculateOutput,
        kStateSteps_Default_NumSteps
    };


    // Imagine more lists for other states here, with comments...

    // Becoming quite long...


    // Private functions used in implementing various processing steps 
    //      (some are used by multiple state-steps in varying ways)
    void PrivateFunction1();
    void PrivateFunction2();


    // Some member variables here
};

这用于实时处理环境中,以便在执行块处理任务时更好地平衡 DSP 负载。实际上,这个类继承自一个基类,该基类处理对 Process 的调用的实际调度,并根据需要更新当前状态和状态步骤。Process() 然后由一个 switch 语句组成,该语句根据对象的当前状态和状态步骤执行某些处理功能和 IO。

枚举中声明的值在 Process() 和 processor.cpp 中的其他私有成员函数中使用,其他任何地方都没有。我已将它们声明为私有成员变量,以将它们限定在类内。有没有办法在 .cpp 中声明它们并实现相同的范围?这些都是在编译时优化的常量整数,本质上被用作 #define 的 - 我只是不想使用宏。

4

4 回答 4

1

所有包含都只是文本包含。由于您包含的文件包含 C++ 语法,因此它应该具有 C++ 标头扩展名(.h 或 .hpp 等)。

您可能不需要将它包含在声明中(如果您发布一些代码,我可以更肯定地说)...您可以将它包含到实现文件中,并将任何枚举成员变量声明为int...使用 typedefs ( )的别名,int如果你想给他们描述性的类型名称。或者,如果您使用的是 C++11,则可以在不定义枚举类型的情况下前向声明它们,然后您的枚举成员变量将是类型安全的,从而防止分配错误类型的枚举值。

至于您是否有理由不将枚举从类声明中移出并包含该文件的问题:人们总是可以发明不做某事的理由,例如“我们的编码标准说永远不要包含文件顶部以外的文件”,但如果这些任意原因不适用于您,那么不,没有理由。在代码可维护性方面做最有意义的事情。

于 2013-02-28T04:14:37.103 回答
0

最后,在获得将枚举细节分离到 .cpp 实现文件中的好处的同时,实现等效功能的最佳方法似乎是在类的私有部分中使用结构的前向声明,然后定义它结构以包含 .cpp 文件中所需的枚举。

// Processor.h

class Processor
{
public:

    Processor();
    void Process( float* input, float* output, int numSamples );

private:

    struct States;     // Used to scope State enum to within class
    struct StateSteps; // Used to scope StateStep enums to within class

    // Other stuff...
}

// Processor.cpp

struct Processor::States
{
    enum
    {
        Default,
        SettingChangeImmediate,
        SettingChangeCrossfade,
        SpecialProcessing,
        NumStates
    };
}

struct Processor::StateSteps
{
    enum
    {
        Default_PrepareInput,
        Default_CalculateIntermediateValues,
        Default_CalculateOutput,
        Default_NumSteps
    };

    enum 
    {
        SettingChangeImmediate_FirstStep,
        // ... other state-steps...
    };
};

这就是为什么我认为这种结构在这个特定用例中是最好的:

  1. 所有枚举列表都被移到 .cpp 文件中,根据需要移出标题的中间,并且可以将包含相同值(例如,从 0 开始计数)的其他 StateStep 枚举添加到 StateSteps 结构的定义中而不会干扰.h 标头(虽然我们可以将条目添加到 forward-declared enum class,但我们不能重复相同的值,并且需要enum class在标头中添加另一个)。

  2. 所有枚举都像以前一样在类的私有部分范围内(尽管也在另一个结构内)。

  3. 用于定义编译时整数常量的枚举可能保持匿名且不是强类型enum class构造,这可能会误导其他人如何使用枚举(在当前用例中,我们希望能够比较不同的 stateStep枚举值到相同的整数currentStep,具体取决于当前状态,就像我们可以使用最初定义的匿名枚举一样)。

以前的答案帮助我得出了这个结论,但我觉得这是一种最接近复制原始定义功能的方式,同时将它们从 .h 文件中移出!

于 2013-03-01T00:20:30.470 回答
0

在课堂中使用 an#include是非常不规则的,可能会导致问题。如果你在它们自己的命名空间或类中声明你的常量会更好。

例如,这是一个坏主意:

class Foo
{
   #include "foostuff.h"
};

更典型的模式是:

#include "foostuff.h"

class Foo
{
   void bar(int x = FooStuff::const_x);
};

在里面foostuff.h你会小心地命名事物,这样它们就不会与你应用程序的其他部分发生冲突。

C++ 的做事方式鼓励在应用程序的不同部分之间重复使用常量,而不是使用#define创建宏,这些宏一旦扩展就没有特定的关联。

所有“包含”文件都应该.h适用于纯 C 或.hpp任何需要支持 C++ 的编译器来解释的文件。其他任何事情都是非标准的,并且至少会导致任何必须维护您的代码的人的蔑视。

于 2013-02-28T04:21:50.263 回答
0

新的 C++11enum class可能会被前向声明,并且真正的定义被移到实现中。这将清理混乱并减少烦恼。

// Procesor.hpp
class Processor
{
public:

    Processor();
    void Process( float* input, float* output, int numSamples );

private:

    // Type of possible states
    enum class kState;
    kState somethingDealingWithState( kState s ); 
};

// Processor.cpp
// List of possible states
enum class Processor::kState
{
    Default,
    SettingChangeImmediate,
    SettingChangeCrossfade,
    SpecialProcessing,
    NumStates
};

Processor::kState Processor::somethingDealingWithState( kState s )
{
    if ( s == kState::Default )
    {
        return kState::SpecialProcessing;
    }
    return kState::Default;
} 
于 2013-02-28T06:06:14.743 回答