【问题标题】:if i want to build logger class in c++ or java what should it be singletone or static如果我想在 c++ 或 java 中构建记录器类,它应该是单调的还是静态的
【发布时间】:2011-02-04 04:27:14
【问题描述】:

一般问题是我喜欢构建写入单个日志文件的记录器类 来自我的应用程序中的不同类,记录器类应该是什么 单调或静态类

【问题讨论】:

标签: c++ static singleton logging


【解决方案1】:

是什么让您认为两者都应该如此?可以按需实例化的常规非静态类怎么样?然后将它的单个静态 instance 用作默认记录器。这样您就可以两全其美:方便地全局访问记录器测试记录器或临时使用不同记录器的能力。

另一个建议是简单地创建一个实例并将其作为参数传递给类的每个组件,正如@disown 所建议的那样。

但是,如果您将类本身设为静态或单例,那您就是在自取其辱。

编辑
例如,回应@Stephen 的评论:

// define a logger class, a perfectly ordinary class, not a singleton, and without all static members
class Logger {
 // ....
};

// create a single global *instance* of this class
Logger defaultLog;

void foo() {
  // and now use the globally visible instance
  defaultLog.Log("hello");

  // or create a new one if that's what we need:
  Logger newlog;
  newlog.Log("hello");
}

没有魔法。这正是标准库所做的。 std::cout 不是单身人士。它只是类std::ostream 的全局实例,该类也可以在需要时正常实例化。

【讨论】:

  • 我不确定我是否理解您的建议。您是否希望每个需要它的类都创建一个新的 logger 对象来进行日志记录?使用单个“defaultLogger”实例与单例有何不同?您的解决方案如何比替代方案更好地为您提供“测试能力”?如果每个人都在实例化自己的记录器,你的系统怎么能“临时使用不同的记录器”呢?奇怪的是你得到了 +4 而 disown 得到了 -1。
  • 翻译单元之间全局变量的构造和销毁顺序未定义。如果您记录的任何代码使用从 ctor 或 dtor 记录的静态或全局变量,这将导致未定义的行为。
  • 是的。但如果发生这种情况,我会说你有一个更根本的问题。所谓的“为什么你有这么多复杂的初始化/销毁行为的复杂静态对象?” 那么解决方案不是在中创建更多复杂的静态数据单例的形式,但要消除你的一些静态。你的程序应该在 main 函数中运行,而不是在它之前。
【解决方案2】:

在 C++ 中,您需要的是单例而不是静态的。 C++ 不允许您控制静态对象的构造和销毁顺序,如果您在构造静态对象之前尝试记录,则行为可能是未定义的。

我不确定 Java。

【讨论】:

    【解决方案3】:

    答案当然是“视情况而定”。

    首先,如果我是你,我不会重新发明轮子,只使用Log4j。如果它不能满足您的确切要求,您最好扩展 log4net 的一个不满足您需求的组件(例如自定义登录源),而不是从头开始。其次,静态类对于简单的日志记录类可能已经足够了。对于更复杂的事情,单例类可能是要走的路。

    【讨论】:

      【解决方案4】:

      在 C++ 中,您可以使用以下惯用语进行延迟初始化:

      Foo& getInstance()
      {
          static Foo instance;
          return instance;
      }
      

      【讨论】:

        【解决方案5】:

        不要使用单例或静态,使用依赖注入,即在 main() 中实例化一个实例,并将对该实例的引用传递给所有依赖类。静态和/或单例几乎从不需要,而且通常会导致代码不太优雅。

        【讨论】:

        • 听起来不像en.wikipedia.org/wiki/Dependency_injection中描述的DI。而且我不认为向每个函数和类显式传递一个非重要参数比在必要时请求一个更优雅。
        • 为什么这被否决了?无论它是否符合 DI 的条件,这都是非常明智的建议。唯一的缺点是在整个程序中传递记录器引用可能有点麻烦。 +1 我来补偿。这并不总是最好的策略,但它是一个完全有效的策略,并且比在你的应用程序中涂抹单例要好得多。
        【解决方案6】:

        我可能会使用单例,但无论哪种方式,都将其隐藏在函数或宏后面。不要让人打字

        MySuperSpectacularLogger::GetInstance()->Log("adssdf");
        

        改为使用:

        void Log(cpnst string& msg) {
          MySuperSpectacularLogger::GetInstance()->Log(msg);
        }
        

        甚至更好:

        // Logs 'msg' with the filename and line number that generates it.
        #define LOG(msg) \
          MySuperSpectacularLogger::GetInstance()->Log(__FILE__, __LINE__, msg);
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-04-20
          • 2021-04-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多