C ++ проверка одноэлементных указателей - PullRequest
3 голосов
/ 30 июня 2010

У меня есть приложение, которое имеет (Qt C ++) класс одноэлементного логгера. Реализация GetInstance ():

if(m_Instance == NULL)
{
    try
    {
        m_Instance = new Logger();
    }
    catch(...){}
}
return m_Instance;

Теперь у меня есть следующий макрос в файле .h: "#define LOG Logger :: Instance () -> Log"

Все хорошо, пока работает операция new (). Каков наилучший способ убедиться, что указатель установлен (я думал, что некоторые блоки try-catch перехватывают std :: bad_alloc, но я не знаю, как реализовать это в макросе)? Я создал обходной путь, который, кажется, работает, но не очень хорош:

"# define LOG if (Logger :: Instance ()) Logger :: Instance () -> Log"

Кроме того, я хотел бы знать, что если в моем объекте много методов получения / установки (например, setPath (), getSize () ...)? В настоящее время у меня есть макрос:

"# define SET Settings :: Instance ()"

и в коде я могу использовать SET-> setPath ("abc"); или SET-> getSize ();

В этом случае мой уродливый обходной путь не работает, потому что для каждого метода требуется отдельный макрос. Любые советы, как я могу улучшить это?

Спасибо за ответы.

Ответы [ 7 ]

9 голосов
/ 30 июня 2010

Не используйте ужасный шаблон дизайна -> не получите проблем. Обычно люди используют что-то более похожее на

Logger& Logger::GetInstance() {
    static Logger instance; 
    return instance;
}

Но картина синглтона вообще ужасна. Вместо этого используйте шаблон экземпляра приложения.

3 голосов
/ 30 июня 2010

Прежде всего, вы действительно хотите, чтобы ваше приложение молча игнорировало все, что вы делаете с этими синглетами, если они не будут выделены?Для чего-то вроде логгера вы можете использовать его для сообщения об исключениях, поэтому, возможно, вы не хотите, чтобы он тоже генерировал.

В таких случаях рассмотрите возможность вызова метода экземпляра при запуске приложения (например, в вашей основной точке входа), чтобы в случае успешного запуска приложения всегда был под рукой регистратор, например

Наконец, я рекомендую, чтобы m_instance использовал что-то вроде boost :: scoped_ptr, чтобы вы не теряли память (или просто сохраняйте регистратор как статический объект в методе экземпляра без косвенного указателя).Конечно, современные операционные системы, как правило, будут очищаться после вас, но если вы начнете выполнять анализ памяти для проверки на утечки, вы получите много ложных срабатываний таким образом.

Что касается этих макросов, ядумаю, что они зверские, но это только мое мнение.Подобные эффекты можно достичь без макросов:

void write_to_log(Logger* logger, const char* msg)
{
    if (logger)
        logger->log(msg);
}

или даже:

void write_to_log(const char* msg)
{
    Logger* logger = Logger::instance();
    if (logger)
        logger->log(msg);
}
2 голосов
/ 30 июня 2010

Вы не говорите, что хотите сделать, если у вас нет журнала.

Если вы хотите, чтобы ошибка распространялась вверх, либо не перехватывайте исключение, либо выбрасывайте более конкретное, чтобы указать отсутствие журнала.

Если вы хотите продолжить независимо, либо используйте нулевую проверку при каждом использовании, либо используйте шаблон нулевого объекта , чтобы заменить процедурную проверку виртуальным методом.

1 голос
/ 30 июня 2010

Если вы уверены в одноэлементном паттерне (который, похоже, здесь используется слишком много), то:

struct ILogger {
    void Log(const char *message) = 0;
};

struct NullLogger : ILogger {
    void Log(const char *message) { }
};

struct Logger : ILogger {
private:
    static ILogger *m_instance;
    static const NullLogger m_null_instance;

public:
    void Log(const char *message) { /* ... */ }

    static ILogger *Instance() {
        if(m_Instance != &m_null_instance) return m_Instance;

        try { m_Instance = new Logger(); } catch(...) { }

        return m_Instance;
    }
};

const NullLogger Logger::m_null_instance;
ILogger *Logger::m_instance = &Logger::m_null_instance;

/* ... */
#define LOG Logger::Instance()->Log
LOG("Hey!");
0 голосов
/ 30 июня 2010

Спасибо за ответы.

Моя одноэлементная реализация создана на основе различных примеров, доступных в Интернете.(например, http://www.yolinux.com/TUTORIALS/C++Singleton.html довольно близко к моему).

Причина, по которой я использую синглтон, заключается в том, чтобы убедиться, что существует только один экземпляр класса (логгер / настройки), и да, я знаюоно используется слишком часто.

Однако, похоже, мне нужно пойти и попытаться реализовать нечто подобное без использования синглтона.

0 голосов
/ 30 июня 2010

(1) Как уже упоминалось: не используйте синглтоны.(2) Если вы используете синглтон, используйте его с надежной реализацией.(3) Если вы хотите взломать:

void Logger::Log( /*params*/ )
{
  if( NULL != this )
  {
    // do your logging
    ...
  }
}
0 голосов
/ 30 июня 2010

Вы уже поймали исключение в своем методе GetInstance, поэтому в макросе ничего не появится.Возможно ...

if(m_Instance == NULL)
{
    try
    {
        m_Instance = new Logger();
    }
    catch (std::bad_alloc &ba)
    {
        // do something here...
    }
    catch(...){}
}

return m_Instance;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...