从其他不相关的 classes 中使用 C++ Log class
Using C++ Log class from within other non-related classes
我有一个 C++ 日志 class(实现很长,可能并不重要),我在其中重载了 << 运算符,因此我可以使用 log << "Error" << endl;
之类的语句使其更直观(并允许我将输出分叉到屏幕和日志,如果需要的话)。 class 工作正常;我在 Main()
中实例化它并且可以毫无问题地使用它。我希望能够在其他不相关的 classes 中使用它,而无需实际将其传递给它们(通过 class 构造函数或类似方法),因为如果我通过副本传递,我最终 运行 某些内部管理日志 class 方法两次,如果我通过引用传递,我必须取消引用外部 classes 中的指针才能使用它 (*log << "Error" << endl;
)。这可能吗?最好的方法是什么?
我知道面向对象的纯粹主义者反对以下想法的所有原因,但它对我在各种大型项目中的日志记录和类似活动都很有效。它避免了其他设计中的一些实际问题:
为您的日志 class 可以做的事情,使用纯虚拟方法创建一个基础 class。创建基 class 的静态成员,它将是指向 class 的单例实例化的指针,该指针由构造函数填充,并可通过静态访问器方法使用。所以您通常将其用作:
base_logger::singleton() << whatever...
实际的记录器 class 构造一次(在第一次使用前方便的地方),它隐式调用 base_logger 构造函数锁定在指向自身的指针中,该指针将作为该单例的引用返回() 功能。
您可能需要 base_logger
和 singleton
不同的名称。我只是描述它是如何组合在一起以方便使用的,而不是建议那些名称。
您可以做的一件事是使用静态单例设计模式制作记录器class。
这意味着它只会创建一个实例(通常在 logger.cpp
编译单元中的静态存储持续时间),并且 class logger 有一个全局可见的静态方法returns 对此的引用。然后,您不需要将对象传递给构造函数或函数或其他任何地方——无论您想在何处使用它,您都可以简单地获取单例。
如果过度使用,这种模式确实通常被认为是有害的。然而,对于记录器实用程序来说,它似乎非常合适——记录器可以非常合理地被视为应用程序级资源,可以适当地绑定到程序的生命周期。单例模式会使创建适当的单元测试变得很痛苦,但通常您也不会非常关心记录器框架的单元测试——如果您的日志消息无法正常工作,通常您会立即发现。
我会尽可能简单,并实例化一个全局 Log
对象(实际上不是单例):
Log log;
并在通用头文件中声明extern
:
extern Log log;
有了这个,您可以在任何地方使用现有的语法。
使用以下声明创建一个全局函数:
Log& log();
它的实现可以是这样的:
Log& log()
{
static Log log;
return log;
}
或者像这样,避免销毁顺序问题(当另一个具有静态存储持续时间的对象的析构函数向日志写入内容时可能会发生):
Log& log()
{
static Log* log = new Log; // never deleted
return *log;
}
然后您可以使用与以前几乎相同的语法来使用您的记录器:
#include "log.h"
// ...
void f()
{
log() << "Error\n";
}
与单例方法相比,这有两个优点:
- 更简单。
- 它将日志的实际关注 class(写入日志消息)与特定的分配策略分开。
顺便说一下...
because if I pass by copy, I end up running certain housekeeping log
class methods twice
不清楚你的意思,但是如果你通过复制,那么class需要是可复制的,复制一个封装或访问外部资源的记录器通常没有意义。这就是为什么标准流 classes(std::ostream
等)不可复制。
and if I pass by reference, I have to dereference the pointer in the external classes to use it
没有。您混淆了指针和引用。它们是完全不同的语言特征。取消引用是针对指针,而不是引用。
我有一个 C++ 日志 class(实现很长,可能并不重要),我在其中重载了 << 运算符,因此我可以使用 log << "Error" << endl;
之类的语句使其更直观(并允许我将输出分叉到屏幕和日志,如果需要的话)。 class 工作正常;我在 Main()
中实例化它并且可以毫无问题地使用它。我希望能够在其他不相关的 classes 中使用它,而无需实际将其传递给它们(通过 class 构造函数或类似方法),因为如果我通过副本传递,我最终 运行 某些内部管理日志 class 方法两次,如果我通过引用传递,我必须取消引用外部 classes 中的指针才能使用它 (*log << "Error" << endl;
)。这可能吗?最好的方法是什么?
我知道面向对象的纯粹主义者反对以下想法的所有原因,但它对我在各种大型项目中的日志记录和类似活动都很有效。它避免了其他设计中的一些实际问题:
为您的日志 class 可以做的事情,使用纯虚拟方法创建一个基础 class。创建基 class 的静态成员,它将是指向 class 的单例实例化的指针,该指针由构造函数填充,并可通过静态访问器方法使用。所以您通常将其用作:
base_logger::singleton() << whatever...
实际的记录器 class 构造一次(在第一次使用前方便的地方),它隐式调用 base_logger 构造函数锁定在指向自身的指针中,该指针将作为该单例的引用返回() 功能。
您可能需要 base_logger
和 singleton
不同的名称。我只是描述它是如何组合在一起以方便使用的,而不是建议那些名称。
您可以做的一件事是使用静态单例设计模式制作记录器class。
这意味着它只会创建一个实例(通常在 logger.cpp
编译单元中的静态存储持续时间),并且 class logger 有一个全局可见的静态方法returns 对此的引用。然后,您不需要将对象传递给构造函数或函数或其他任何地方——无论您想在何处使用它,您都可以简单地获取单例。
如果过度使用,这种模式确实通常被认为是有害的。然而,对于记录器实用程序来说,它似乎非常合适——记录器可以非常合理地被视为应用程序级资源,可以适当地绑定到程序的生命周期。单例模式会使创建适当的单元测试变得很痛苦,但通常您也不会非常关心记录器框架的单元测试——如果您的日志消息无法正常工作,通常您会立即发现。
我会尽可能简单,并实例化一个全局 Log
对象(实际上不是单例):
Log log;
并在通用头文件中声明extern
:
extern Log log;
有了这个,您可以在任何地方使用现有的语法。
使用以下声明创建一个全局函数:
Log& log();
它的实现可以是这样的:
Log& log()
{
static Log log;
return log;
}
或者像这样,避免销毁顺序问题(当另一个具有静态存储持续时间的对象的析构函数向日志写入内容时可能会发生):
Log& log()
{
static Log* log = new Log; // never deleted
return *log;
}
然后您可以使用与以前几乎相同的语法来使用您的记录器:
#include "log.h"
// ...
void f()
{
log() << "Error\n";
}
与单例方法相比,这有两个优点:
- 更简单。
- 它将日志的实际关注 class(写入日志消息)与特定的分配策略分开。
顺便说一下...
because if I pass by copy, I end up running certain housekeeping log class methods twice
不清楚你的意思,但是如果你通过复制,那么class需要是可复制的,复制一个封装或访问外部资源的记录器通常没有意义。这就是为什么标准流 classes(std::ostream
等)不可复制。
and if I pass by reference, I have to dereference the pointer in the external classes to use it
没有。您混淆了指针和引用。它们是完全不同的语言特征。取消引用是针对指针,而不是引用。