Qt C ++:全局对象与参考链

时间:2010-06-30 10:53:59

标签: c++ qt singleton global-variables

目前,我正在为我的应用程序中的某些全局对象使用单例模式(用于Symbian环境的Qt应用程序)。但是,由于一些问题(C++ checking singleton pointers),看起来我必须改变逻辑。

我需要通过多个不同的对象访问3个类(记录器,设置和一些临时数据的容器)。目前,它们都是使用单例模式创建的。当设置和容器具有多个具有一些附加逻辑的get / set方法(例如QFileSystemWatcher)时,记录器基本上只是一个具有一些内部逻辑的公共方法Log()。此外,记录器和设置有一些交叉引用(例如,记录器需要一些设置和设置记录错误)。

目前一切都“工作正常”,但仍然存在一些应该注意的问题,并且看起来它们不容易为单例实现(可能的内存泄漏/空指针)。现在我有两种不同的方法来处理这个问题:

  1. 创建全局对象(例如extern Logger log;)并在应用程序启动时初始化它们。
  2. 在我的主对象中创建对象,并将它们作为参考传递给子对象。
  3. 我对这些问题的解答很少:

    案例1。

    • 使用堆栈还是堆更好?
    • 我将使用extern关键字在某些globals.h标头中声明这些对象。好吗?
    • 我认为在这种情况下我必须删除该双向引用(设置需要记录器,反之亦然。)?

    案例2。

    • 是否应在主对象的堆栈或堆中创建对象(例如Logger * log = new Logger()vs Logger log;)

    • 长引用链看起来不太好(例如,如果我必须将对象传递给多个孩子)。

    • 小孩怎么样?

      1. 如果我将指针传递给这样的孩子(我不想复制它,只需使用“引用”):Children(Logger * log):m_Log(log)当孩子被删除时会发生什么?我应该将本地指针m_Log设置为NULL还是?
      2. 如果我使用堆栈,我会发送对子项的引用(Children(Logger& log):m_Log(log))其中m_Log是引用变量(Logger& m_Log;)对吗?
    • 在这种情况下,我应该注意Qt内存管理?

    案例3。 继续单例并在启动期间初始化单例对象(这将解决空指针)。那么唯一的问题就是内存泄漏。我的实现遵循this示例。当我使用时访问该类时是否存在可能的内存泄漏。单身破坏怎么样?     #define LOG Logger :: Instance() - > Log

    感谢阅读。

2 个答案:

答案 0 :(得分:1)

答案 1 :(得分:-2)

我发现另一个答案有点误导。这是我的,希望SO社区将确定哪个答案更好:

  

使用堆栈还是堆更好?

假设“堆栈”你的意思是全局(因此在数据段中),不要担心它,做任何更容易的事情。请记住,如果在堆上分配它,则必须调用delete。

  

我将使用extern关键字在某些globals.h标头中声明这些对象。可以吗?

为什么你需要这样做?唯一需要访问全局变量的类是单例本身。全局变量甚至可以是静态局部变量,ala:

class c_Foo
{
    static c_Foo& Instance()
    {
        static c_Foo g_foo; // static local variable will live for full life of the program, but cannot be accessed elsewhere, forcing others to use cFoo::Instance()
        return g_foo;
    }
 };

如果你不想使用static local,那么c_Foo类型的私有静态成员变量(在我的例子中)比直接全局更合适。

请记住,您希望类的生命周期为“全局”(即在应用程序退出之前不会被销毁),而不是实例本身。

  

我认为在这种情况下我必须删除该双向引用(设置需要记录器,反之亦然。)

所有被驱逐的全局变量都必须向前声明,但正如我上面所说,你不需要这个头文件。

  

是否应在我的主对象中的堆栈或堆中创建对象(例如Logger * log = new Logger()vs Logger log;)

我无法回答这个问题,不要再担心了。

  

长引用链看起来不太好   (例如,如果我必须传递该对象   超过多个孩子。)

     

孩子怎么样?

好点,你已经意识到这将是一个巨大的痛苦。在类似记录器的情况下,将引用传递给每个模块只是为了让它们可以吐出日志信息是不太可能的。更合适的是单个静态“Log”函数ala C,如果您的记录器具有有用状态,则使其成为仅对您的日志函数可见的单例。您可以在实现日志功能的同一.cpp文件中声明和实现整个记录器类。然后,没有其他任何东西会知道这个特殊功能。

这就是我的意思:

file:log.h

#ifndef LOG_H
#define LOG_H

void Log( const char* data ); // or QString or whatever you're passing to your logger

#endif//LOG_H

file:log.cpp

#include "log.h"

// declare your logger class here in the cpp file:
class Logger
{
// ... your impl as a singleton
}

void Log( const char* data )
{
    Logger.getInstance().DoRealLog( data );
}

这是一个精细的记录器界面,你单身的所有力量,有限的大惊小怪。

  

使用时访问该类时是否存在内存泄漏。单身破坏怎么办?

如果您在多线程环境中懒惰地在堆上分配单例,则存在双重实例化的风险。这会导致多余的实例泄漏。

如果您使用静态本地,则存在一些类似的风险:http://blogs.msdn.com/b/oldnewthing/archive/2004/03/08/85901.aspx

在我看来,执行开始时的显式构造是最好的选择,但是它是在数据段中完成(在类或全局变量中使用静态变量)还是堆在您的情况下不太重要。如果你确实在堆上分配它,你也应该删除它。