我的Qt项目中有一个用于记录目的的单例类。在除单例之外的每个类中,有一个指向单例对象的指针点和一个连接到单例对象中的写入槽的信号。无论哪个类想要写日志信息都会发出该信号。信号排队,因此它是线程安全的。
请从OOP的角度批评这种方法,谢谢。
=============================================== ============================================== 编辑1: 谢谢你所有的申请,听取相反的意见总是一个很大的学习。
让我解释一下我的方法以及到目前为止我在代码中所做的事情: 正如MikeMB指针一样,单例类有一个像get_instance()这样的静态函数,它返回对该单例的引用。我将它存储在每个类的构造函数中的本地指针中,因此它将在构造函数返回后被销毁。检查我是否有一个空指针并使代码更具可读性是很方便的。我不喜欢这样的东西:
if(mySingletonClass::gerInstance() == NULL) { ... }
connect(gerInstance(), SIGNAL(write(QString)), this, SLOT(write(QString)));
因为它比这更贵:
QPointer<mySingletonClass> singletonInstance = mySingletonClass::getInstance();
if(singletonInstance.isNull) { ... }
connect(singletonInstance, SIGNAL(write(QString)), this, SLOT(write(QString)));
由于推送,弹出和返回地址计算,调用函数两次比从ASM的角度创建局部变量更昂贵。
这是我的单身人士课程:
class CSuperLog : public QObject
{
Q_OBJECT
public:
// This static function creates its instance on the first call
// and returns it's own instance just created
// It only returns its own instance on the later calls
static QPointer<CSuperLog> getInstance(void); //
~CSuperLog();
public slots:
void writingLog(QString aline);
private:
static bool ready;
static bool instanceFlag;
static bool initSuccess;
static QPointer<CSuperLog> ptrInstance;
QTextStream * stream;
QFile * oFile;
QString logFile;
explicit CSuperLog(QObject *parent = 0);
};
我在main()的开头调用了getInstance(),因此请确保每当需要记录重要信息时,立即为每个其他类读取它。
MikeMB:
Your approach is making a middle man sitting in between, it makes the path of the logging info much longer because the signals in Qt are always queued except you make direct connection. The reason why I can't make direct connection here is it make the class non-thread-safe since I use threads in each other classes. Yes, someone will say you can use Mutex, but mutex also creates a queue when more than one thread competing on the same resource. Why don't you use the existing mechanism in Qt instead of making your own?
谢谢你们的所有帖子!
=============================================== ==========
致Marcel Blanck:
您能否详细解释一下您的静态Logger工厂方法?感谢。
答案 0 :(得分:2)
我不太喜欢单身人士,因为使用它们总是不洁净的。我甚至读过那些说“设计模式知识,同时知道单身人士不能使用”的职位描述。 Singleton导致依赖地狱,如果你想改变一个完全不同的日志记录方法(mabe用于测试或生产),而不是破坏旧的,你需要改变很多。
approch的另一个问题是信号的使用。是免费获得线程保存,并且不要中断代码执行,但是......
我会直接打印。也许您可以拥有一个返回记录器的静态Logger工厂,这样您就可以在每个线程中拥有一个记录器对象(内存影响仍然非常小)。或者你有一个使用信号量的线程,并有一个静态接口。在这两种情况下,记录器都应该通过接口使用,以便以后更灵活。
还要确保您的方法直接打印。甚至printf在打印之前写入缓冲区,你需要每次刷新它,否则你可能永远不会在恶劣的情况下发现崩溃,如果它们正在寻找它们。
只需2美分。
答案 1 :(得分:1)
单例通常有一个静态函数,如get_instance()
,它返回对该单例的引用,因此您不需要在每个对象中存储指向单例的指针。
此外,让每个对象将其日志信号连接到日志记录对象本身的日志记录槽是没有意义的,因为这会使项目中的每个类都依赖于您的日志记录类。相反,让一个类只发出带有日志信息的信号,并在更高级别的中心位置建立连接(例如,在主函数中设置系统时)。因此,您的其他类不必知道谁在监听(如果有的话),您可以轻松地修改或替换您的日志记录类和机制。
顺便说一句:那里已经有非常先进的日志库了,所以你应该知道你是否可以使用其中一个或至少使用它们,并根据你的需要调整这个概念。==========================
编辑1(对QtFan编辑1的回应):
很抱歉,显然我很想念你,因为我认为指针是一个类成员而不仅仅是构造函数中的局部变量,当然这很好。
让我通过在更高层次上建立连接来澄清我的意思: 这完全针对您建立连接的位置 - 即您放置线的位置
connect(gerInstance(), SIGNAL(write(QString)), this, SLOT(write(QString)));
我建议把它放在课外的某个地方,例如进入主要功能。所以伪代码看起来像这样:
void main() {
create Thread1
create Thread2
create Thread3
create Logger
connect Thread1 signal to Logger slot
connect Thread2 signal to Logger slot
connect Thread3 signal to Logger slot
run Thread1
run Thread2
run Thread3
}
这样做的好处是,您的类不必知道您正在使用的记录器类型,以及是否只有一个或多个或根本没有。我认为关于信号和时隙的整个想法是发射对象不需要知道其信号的处理位置,接收类不必知道信号来自何处。
当然,如果您在程序运行时没有动态创建对象/线程,这只是可行的。如果要在创建对象期间进行记录,它也不起作用。
答案 2 :(得分:1)
我会考虑分离记录器应该是唯一的,以及其他类如何获取记录器类的实例。
创建和获取记录器的实例可以在某种工厂中处理,该工厂在内部封装其构造,并且只在需要时才生成一个实例。
然后,其他类获取记录器实例的方式可以通过依赖注入或上述工厂中定义的静态方法来处理。使用依赖注入,首先创建记录器,然后在创建后将其注入其他类。