c++:logger类,不带全局变量或singleton,或将其传递给每个方法
c++: logger class without globals or singletons or passing it to every method
有人知道是否可以在没有的情况下拥有像记录器这样的类吗
-
使用单例或全局(a la std::cout(
-
将实例/指针/引用传递给每个需要它的方法
我举了一个logger类的例子,但我的应用程序中有几个类会从中受益(例如,undo管理器(。
每个解决方案都有几个问题:
-
使用singleton对测试来说是有问题的(还有很多原因,使用singleton通常不是一个好主意(。全球化也是如此。此外,没有什么能保证应用程序中只有一个实例,而且这甚至不是一个要求(例如,为什么不有两个记录器?(
-
将它传递给每个对象构造函数(依赖项注入(,会导致大量的样板代码,并且很容易出错,因为您必须多次复制/粘贴相同的代码。是否可以认真考虑在每个类的构造函数中都有一个指向Logger的指针???????
所以我想知道是否还有第三种选择,在C++中,我从未听说过?对我来说,这听起来需要一些黑魔法,但我对我在堆栈溢出中学到的一些技术感到惊喜,这些技术在谷歌上找不到,所以我知道这里有一些真正的大师;(
令人惊讶的是,我发现了很多关于如何设计singleton,或者为什么不应该使用singleton的讨论,但我找不到一篇解决我问题的帖子。。。
我想你可以用Log4j包做一些类似于Java中做的事情(可能用它的Log4c版本做(:
有一个可以返回多个记录器实例的静态方法:
Logger someLogger = Logger.getLogger("logger.name");
getLogger()
方法没有返回singleton对象。它正在返回命名的记录器(如果需要,则创建它(。Logger
只是一个接口(在C++中,它可能是一个完全抽象的类(——调用者不需要知道正在创建的Logger
对象的实际类型。
您可以继续模拟Log4j,并有一个getLogger()
的重载,它也接受一个工厂对象:
Logger someLogger = Logger.getLogger("logger.name", factory);
该调用将使用factory
来构建记录器实例,使您能够更好地控制正在创建的底层Logger
对象,这可能有助于您进行嘲讽。
因此,不需要将任何东西传递到您自己代码的构造函数、方法等中。您只需在需要时获取所需的命名Logger
并登录到它。根据您编写的日志代码的线程安全性,您可以将getLogger()
返回的内容作为类的静态成员,因此每个类只需调用getLogger()
一次。
一个带有一些静态方法的类怎么样?
class Logger
{
public:
static void record(string message)
{
static ofstream fout("log");
fout << message << endl;;
}
...
};
...
void someOtherFunctionSomewhere()
{
Logger::record("some string");
...
}
没有Singleton,没有全局变量,但任何可以看到Logger.h
的代码都可以调用成员函数,如果所有公共成员函数都返回void
,那么就很容易进行测试。
我认为C++中没有一个好的替代方案,但Emacs Lisp中有一个可能值得考虑:变量的动态绑定。基本概念是这样的,当你引用一个变量时,你不一定会通过名称访问全局变量,而是通过相同的名称访问执行路径中最后定义的变量。在伪C++代码中,这看起来像这样:
// Pseudo-C++ with dynamic binding
Logger logger = Logger("GlobalLogger");
void foo() { logger.log("message"); }
int main()
{
foo(); // first
{
Logger logger = Logger("MyLogger");
foo(); // second
}
foo(); // third
}
在这个伪C++示例中,当您第一次调用foo()
时,将使用GlobalLogger,而当您第二次调用它时,将调用MyLogger,因为只要全局变量在作用域内,即使在foo()
内,MyLogger也会覆盖全局变量。第三个调用将返回到GlobalLogger,因为MyLogger退出了作用域。这允许为选定的代码段使用自定义记录器覆盖全局记录器,而无需在所有代码中传递记录器对象,也无需设置全局变量。
真正的C++没有动态绑定,但应该可以复制它的各个方面:
std::stack<Logger> logger = { Logger("GlobalLogger") };
void foo() { logger.top().log("message"); }
int main()
{
foo();
{
logger.push(Logger("MyLogger"));
foo();
logger.pop();
}
foo();
}
为了进一步清理,堆栈应该隐藏在DynamicBinding
类中,手动.push((/.pop((操作可以隐藏在作用域保护后面,并且需要处理多线程问题。但是,与简单的单例变量或全局变量相比,基本概念可能会起作用,并提供更大的灵活性。
- 为不同配置设置MSVC_RUNTIME_LIBRARY的正确方法是什么
- 通过方法访问结构
- 最小硬币更换问题(自上而下方法)
- C++为构建时间获取QDateTime的可靠方法
- 在C#中处理C++指针而不使用unsafe的最佳方法
- 如何将Singleton类信号连接到MainWindow类插槽或方法
- 在用户类中包括PYBIND11嵌入式模块的正确方法(作为Singleton类的一部分)是什么?
- Singleton实现:与Singleton类相比,名称空间方法通常更可取吗
- 具有模板化类型的动态类型Singleton.这是可行的方法吗?[提供的解决方案]
- 在其方法中获取singleton实例
- 在Cocos2Dx中实现Singleton的正确方法
- Singleton类贯穿整个项目的方法
- 如何在 c++ 中的 Normal 或 Singleton 类中 GoogleTest 私有方法/枚举类
- 构造函数的Singleton方法
- 作为静态字段的Singleton实例与getInstance()方法中的静态变量
- 访问blackberry 10中的singleton类方法
- 如何在不使用非构造函数的方法的情况下应用singleton设计模式来返回类对象
- 使用Singleton类的最有效方法
- 静态方法与singleton
- c++:logger类,不带全局变量或singleton,或将其传递给每个方法