【问题标题】:What's the proper "C++ way" to do global variables?做全局变量的正确“C++方式”是什么?
【发布时间】:2009-04-21 13:58:20
【问题描述】:

我有一个主应用程序类,其中包含一个记录器,以及一些通用的应用程序配置等。

现在我将显示很多 GUI 窗口等(将使用记录器和配置),并且我不想将记录器和配置传递给每个构造函数。

我见过一些变体,比如到处声明主类 extern,但感觉不是很面向对象。使所有(或大多数)其他类可以访问主类中的元素的“标准”C++ 方法是什么?

【问题讨论】:

标签: c++ design-patterns oop


【解决方案1】:

如果您的 logger 和 config 足够抽象,那么将 logger 和 config 传递给所有构造函数并不是一个坏主意。

单例在未来可能会成为一个问题。但这似乎是项目开始时的正确选择。你的选择。如果您的项目足够小 - 使用单例。如果不是 - 依赖注入。

【讨论】:

  • 如果你只是追加到一个文件可能还不错,但是如果你必须初始化一个日志框架,那么你可能不想每次都重新初始化它。
  • 重新初始化?为什么?您在顶部创建一次,然后将其传递到底部给所有需要它的服务。
  • 对不起,我误读了“将记录器和配置传递给所有构造函数……”
  • 或者只是收集您需要传递的内容并将它们全部放入一个类/结构中,然后在一个参数中传递您需要的所有内容。
  • +1 用于依赖注入,esp 对单元测试很有用。我什至将它与单例一起使用以避免 Singleton::instance() 到处调用,恕我直言,客户不应该关心对象是否是单例。
【解决方案2】:

我同意某种单例方法。您绝对不想到处传递记录器对象。这很快就会变得非常无聊,恕我直言,这是一个比仅仅拥有一个普通的全局对象更糟糕的设计。

一个很好的测试你是否有一个好的解决方案是让日志记录在需要它的函数中工作所需的步骤。

如果你要做的远不止

#include "Logger.h"
...
void SomeFunction()
{
    ...
    LOGERROR << "SomeFunction is broken";   
    ...
}
...

那你就是在浪费精力。

【讨论】:

    【解决方案3】:

    日志记录属于“关注点分离”领域,如aspect orient programming

    通常日志记录不是对象的功能或关注点(例如,它不会改变对象的状态;它只是一种观察/记录状态的机制,并且在大多数情况下输出基本上是一次性的) 它是一个短暂且通常是可选的辅助功能,对类的操作没有贡献。 对象的方法可能会执行日志记录,但可能会在此处进行日志记录,因为它是一个方便的地方,或者代码执行流中的那个点是人们希望记录状态的地方。

    因为 C++ 不提供定义方面的工具,所以我倾向于简单地将本质上的外部临时对象(如记录器)保持为全局对象,并将它们包装在命名空间中以包含它们。命名空间不是为了包含而设计的,所以这有点难看,但由于缺乏其他任何东西,它比在形式参数中传递记录器或在要记录的所有对象中引用它们更方便,而且远不那么难看和不方便。如果在某些时候我决定不再需要记录器(即,如果它仅用于调试),这也可以更轻松地删除记录器。

    【讨论】:

    • 同样,在命名空间中使用全局变量可以更轻松地为多线程应用程序实现锁定机制。
    【解决方案4】:

    为什么不使用已有的系统?即重定向std::clog输出到文件,写入std::clog。

    std::fstream *f = new std::fstream("./my_logfile.log")
    
    std::clog.rdbuf(f->rdbuf());
    
    std::clog << "Line of log information" << std::endl;
    

    【讨论】:

    • 您会丢失真实记录器的一致格式(在一个地方定义),并且您可能还会丢失用于输出某些非标准信息的实用程序功能(即没有字符串转换)。但是对于一个小项目来说,这是一个非常快速和简单的方法。
    • 即使你不想使用std::clog,你仍然可以从中学习。 clog 是一个全局变量。如果您想要自己的记录器,请将其设为全局变量。
    • @jalf:它实际上是使用“Construct On First Use”——成语只在需要时才创建实例。
    • @rmeador:格式一致性可以通过自定义流修饰符(例如 clog
    【解决方案5】:

    使用singleton design pattern

    基本上,您返回一个对象的静态实例并将其用于您的所有工作。

    请查看link about how to use a singletonstackoverflow link about when you should not use it

    警告:单例模式涉及提升全局状态。全局状态不好的原因有很多。
    例如:单元测试。

    【讨论】:

    • 但请注意!明智地使用你的单身人士:ibm.com/developerworks/webservices/library/co-single.html
    • 记录器是少数几个可以选择单例的地方之一!
    • 没有。为什么这被接受了?单例和全局是完全不同的东西。如果你需要一些全球性的东西,那就让它成为全球性的。但是,使某些东西全球化并不意味着它也应该仅限于一个实例。顺便说一句,记录器对于单身人士要么 来说不是一个好地方。我之前也是这么想的,然后几个月后,我被诅咒了,因为我不得不添加第二个记录器。它节省了很多工作,只是一开始就不让它成为单例。
    • 它不是一个全局变量,但它绕过了必须使用带有一堆外部变量的全局变量。我并不是建议他首先将他的代码更改为只有一个记录器,这已经存在于他的代码中。但这是一种比他目前使用的更面向对象的方式。反正他现在只有一个。我还提供了关于何时不使用它的 2 个警告链接。
    【解决方案6】:

    我猜Service Locator 可以。您必须在构造函数中传递,或者在某个众所周知的位置具有全局可访问的静态成员函数。前一种选择更可取。

    【讨论】:

      【解决方案7】:

      我会避免使用单例模式。
      测试方面的问题太多了(见What is so bad about singletons?

      我个人会将记录器等传递给构造函数。或者,您可以使用工厂来创建/传递对资源的引用。

      【讨论】:

      • 如果执行得当,在使用#include 的任何地方都可以使用单例和您的替代方案之间确实没有区别,除非您的替代方案必须在每个级别传递引用。
      【解决方案8】:

      不知道这对您的情况是否有帮助,但在 MFC 中,有/现在有一个应用程序类。

      我过去常常把这样的东西扔进那个班级。

      我假设您没有使用 MFC,但如果您有应用程序类或类似的东西,这可能会有所帮助。

      【讨论】:

        【解决方案9】:

        为什么不使用log4cxx? 这些问题很久以前就解决了,并被许多人广泛使用。 除非您正在构建自己的一些非常特殊的日志记录系统......在这种情况下,我会使用工厂模式,它会为任何感兴趣的人创建记录器(或者如果它是单例则放弃现有实例)。其他类将使用工厂来获取记录器。在构造函数参数中传递记录器是个坏主意,因为它会将您的类与记录器耦合在一起。

        【讨论】:

          【解决方案10】:

          为什么没有人想到遗产和多态性?您还可以使用带有该单例的抽象工厂;)

          【讨论】:

            【解决方案11】:

            只需将您的主类传递给您想要访问“一切”的其他类的构造函数

            然后您可以通过成员属性提供对记录器等的访问。 (原谅我的 C++ 语法,这只是一种虚构的语言,叫做“被 VB 混淆的 C++”)

            例如

            Class App {
                 Private  m_logger;
                 Private  m_config;
            
                 Public logger() {
                    return m_logger;
                 }
            
                 Public config() {
                    return m_config
                 }
            }
            
            Class Window1 {
                 New( anApp ) {
                 }
                 ....
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2012-12-15
              • 2022-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-05-04
              • 2020-02-16
              相关资源
              最近更新 更多