【问题标题】:Appropriate use of global const variables in C++?在 C++ 中适当使用全局 const 变量?
【发布时间】:2011-09-19 06:34:36
【问题描述】:

我正在为我的 CS 课程开发一个程序。它模拟了一家快递公司在机场的活动。

这是一个非常简单的小程序,由几个头文件和源文件以及一个编排模拟的 main.cpp 源文件组成。

有一些给定的常数值,例如货物到达的频率、飞机的装载能力、工人处理某些物品所花费的时间等等(都是整数值)。我有必要在 main.cpp 中的多个函数中访问这些变量

在 main() 函数上方将这些声明为 const ints 似乎是合理的,有效地使它们成为全局变量,例如

const int kTotalTime = 2000;
const int kPlaneCapacity = 25;
int main(){//...program code}

我知道在大多数情况下都应避免使用全局变量,因为对它们的调用和/或修改位置没有限制,这可能会导致意外破坏程序的某些部分,进而可能难以调试,以及导致未来代码的兼容性问题等。 然而,由于这些是在整个程序中使用的原始数据类型的只读值,这似乎是一个合理的解决方案。此外,它还向任何阅读代码的人以及编译器明确说明了变量的用途。

问题:我的逻辑有缺陷吗?怎么会这样?什么时候可以合理使用全局变量(常量或非常量)?如果这是一个糟糕的解决方案,那么您如何建议声明诸如此类的常量只读值?

非常感谢您的宝贵时间!

【问题讨论】:

    标签: c++ global-variables constants


    【解决方案1】:

    关于你的程序的大小和目的(我从你的描述中理解)这可能并不重要,但由于它具有教育背景,我建议“做对”。

    在这种情况下,我会选择一个 Config 结构(或类,如果你想让它更聪明一点,请参见下文),它带有配置值,可以在你的程序中随意使用。它的优点是,如果您必须从文件或命令行中获取选项,您可以轻松更改它。

    至于 class 与 struct thingy(请注意,我在这里进行逻辑区分,而不是技术区分)。要么将所有值作为成员放入 struct 并传递它的 const refs,要么将其设为一个完整的类,其中包含隐藏数据来自何处(以及如何生成)的访问器。编程始终是决策,这是您的决定。如果您认为将来必须允许更多的配置可能性(如上所述),您可能需要进行类抽象。

    另一种选择是将数据分散到整个程序中,这实际上比听起来要聪明得多。如果每个类都只知道它的配置选项(并隐藏它们),那么您实际上可以使用您正在使用的 OOP 语言。示例:

    // footype.h
    class FooType {
      private:
        static const int fooOption;
    };
    // bartype.h
    class BarType {
      private:
        static const float barOption;
    };
    

    问题是,如何初始化它。一种方法是创建一个如下所示的config.cpp

    #include "footype.h"
    #include "bartype.h"
    
    const int FooType::fooOption = 42;
    const float BarType::barOption = 7.4;
    

    因此,您可以隐藏信息,并且您仍然可以将所有配置选项集中在一个位置 (config.cpp)。

    编辑:

    如果您有许多(多个)不同模块所需的配置选项,您可以像这样进行一些复杂性(间接):

    // footype.h
    class FooType {
      private:
        static const int& fooOption;
        static const bool& dumpLevel;
    };
    // bartype.h
    class BarType {
      private:
        static const float& barOption;
        static const bool& dumpLevel;
    };
    

    config.cpp:

    #include "footype.h"
    #include "bartype.h"
    
    static const int opt_foo = 42;
    static const float opt_bar = 7.4;
    static const bool opt_dumpLevel = false;
    
    const int& FooType::fooOption = opt_foo;
    const bool& FooType::dumpLevel = opt_dumpLevel;
    const float& BarType::barOption = opt_bar;
    const bool& BarType::dumpLevel = opt_dumpLevel;
    

    如果需要,您甚至可以将选项设置为非 const(但我没有看到可变的 配置选项 中的意义)。

    【讨论】:

    • 类和结构之间几乎没有区别。
    • @Arafangion:存在逻辑上的差异,正如我所指出的。除了技术上的差异之外(struct 成员默认为公共,而class 成员默认为私有)。技术上的差异暗示了你的逻辑:structs 被大多数人认为是简单而愚蠢的数据容器,而classes 被认为是智能对象,它们直接相关操作。
    • 位掩码,我喜欢你对配置结构或类的想法,这似乎是一个更干净、更有条理的解决方案,正如你所说,便于在用户想要修改数据时轻松修改值.我想我会在权衡这个任务的 class 与 struct 的利弊之后选择这条路线。至于在整个程序中分散数据,假设我有一个类 Item 并且在其中我描述了一些普遍可访问的属性,并且该类的每个实例都将共享,我可以公开:static const int (例如)或带访问器的私有? T.Y.
    • 重点是强烈劝阻公共数据成员,除非你有非常非常非常好的理由(你没有)。第二个版本的优点是您只指定一次值(在config.cpp 中定义和声明static 变量时)。然后将该信息传递给类型成员。引用不是必需的,您也可以对这些值进行严格的复制(为了清楚起见,我只是将其包括在内,以便不复制大型的非 pod 常量,但对于 const (!) bools 和 ints 最好不要让成员引用。
    • @ObjectiveCat:两者都可以(假设您选择Config 课程)。但是,如果您没有真正复杂的类型并且您希望为将来的变化保持类型开放,我想我会选择非引用返回值,因为要返回引用,您需要一个保存该值的变量.如果您的 Config 实现碰巧在运行时从文件中获取值,您将必须在本地成员中缓冲它们。
    【解决方案2】:

    我认为最好将常量作为静态常量放在类中。

    我假设你有 Plane 类,只需这样做:

    平面.h

    class Plane{
       static const int kPlaneCapacity;
       //....
    }
    

    Plane.cpp

    const int Plane::kPlaneCapacity = 25;
    

    此外,请妥善保管您对常量的理解。 Pi 是一个常数。 10 是一个常数。我确实知道您会如何认为飞机容量是恒定的,但请考虑一下:如果您的老师说您的下一个作业,您的飞机容量应该是 30 架,而不是 25 架。

    【讨论】:

    • 常量是指程序永远不会修改的值,但我希望它们在阅读代码的人眼中脱颖而出,因此它们遵循 kConstantVariable 命名约定并列在main.cpp 文件的顶部。我可能会遵循位掩码的建议并创建一个配置类,从而隔离变量。实际上,我已经在类中静态定义了一些特定于类的常量。例如,我的 Event 类有各种 conststatic kEventType。简单地将它们公开是否合理?还是建议使用访问器?
    • 感谢您的建议! (在之前的评论中空间不足)。
    【解决方案3】:

    什么时候可以合理使用全局变量(const 或非 const)?

    如果您的程序是多线程程序,那么您应该认真考虑使用全局变量,因为它们需要适当的同步以避免竞争条件强>。通常,正确的同步并不是一项非常简单的任务,需要一些认真的理解和思考。

    这是nice article的摘录:

    非本地化 -- 当单个元素的范围有限时,源代码最容易理解。程序的任何部分都可以读取或修改全局变量,因此很难记住或推理每种可能的用途。 没有访问控制或约束检查——程序的任何部分都可以获取或设置全局变量,并且任何关于其使用的规则都可以很容易地被破坏或遗忘。

    隐式耦合 -- 具有许多全局变量的程序通常在其中一些变量之间存在紧密耦合,并且在变量和函数之间存在耦合。将耦合的项目组合成有凝聚力的单元通常会导致更好的程序。

    内存分配问题 -- 某些环境的内存分配方案使得全局变量的分配变得棘手。在“构造函数”具有分配以外的副作用的语言中尤其如此(因为在这种情况下,您可以表达两个全局变量相互依赖的不安全情况)。此外,当动态链接模块时,可能不清楚不同的库是否有自己的全局实例或全局是否共享。

    测试和限制 - 使用全局变量的源代码更难测试,因为无法在运行之间轻松设置“干净”环境。更一般地,使用任何类型的未明确提供给该源的全球服务的源由于相同的原因而难以测试。

    鉴于以上所有情况,只要您了解陷阱并了解您使用全局变量的方式确实使您的程序免受这些陷阱的影响,那么您就可以继续并很好地使用全局变量。

    【讨论】:

    • 非常好的文章,但与常量值完全无关。
    • 感谢您提供有趣的观点和文章,我正在开发的程序是单线程的并且非常基础,但是一旦我涉足多线程领域,我会记住您的建议。此外,我想强调一个事实,即我专门询问使用 const 全局变量,在我(谦虚但有限)看来,这缩小了通常与全局变量相关的问题的范围。
    • @bitmask:我在答案顶部添加了 OP 的问题 const or not
    • @ObjectiveCat:是的,与const globals 的多线程问题无关。除此之外,文章提到的还有很多相关问题,但我认为这些问题不适用于您的程序,这就是为什么我在答案中提到的原因,只要您知道可以继续并拥有的陷阱那些全局变量。
    • @Als:谢谢先生!澄清一下,尽管我的特定程序处理常量,但我也对使用全局变量的一般后果感兴趣,因为我没有遇到太多关于该主题的信息(因此我的问题),我很欣赏广阔的视角。我在大学课程中听到的唯一一件事是,由于缺乏范围(很明显),全局变量很糟糕。
    猜你喜欢
    • 2012-10-24
    • 1970-01-01
    • 2014-05-10
    • 1970-01-01
    • 2014-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多