2015-08-07 2 views
3

Я реализовал очень простой (ошибочный) класс регистратора. Это выглядит следующим образом:Ошибка регистрации в C++

#pragma once 

#include <string> 

extern const char* LOG_DEFAULT_TEXT = "<LOG>\n\n"; 

class Log 
{ 
public: 
    Log() : _logs(0), _log(LOG_DEFAULT_TEXT) {}; 

    void add(const char *str, bool nl=true); 
    void clear(); 

    const char* get() const { return _log.c_str(); } 
    int getNumLogs() const { return _logs; } 


private: 
    std::string _log; 
    int _logs; 
}; 

Теперь мой вопрос, что у меня есть это главный класс, который содержит все другие объекты, что моя программа может содержать, а также этот класс Log. Очевидно, что я хотел бы, чтобы эти «другие объекты» в моем основном классе могли использовать объект журнала в Main, поэтому простым решением было бы передать указатели на каждый конструктор класса, чтобы он мог использовать объект журнала.

Я говорю о чем-то вроде этого:

//Main.h 
... 

#include "Log.h" 

class Main() { 
public: 
    ... 

private: 
    Log _log; 
    ImportantObject1(&_log); //pass a pointer to _log 
    ImportantObject2(&_log); //..same 
}; 

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

ответ

5

Это один из редких случаев, одноэлементно имеет смысл:

class Log 
{ 
public: 

    void add(const char *str, bool nl=true); 
    void clear(); 

    const char* get() const { return _log.c_str(); } 
    int getNumLogs() const { return _logs; } 

    static Log& instance() { 
     static Log theInstance; 
     return theInstance; 
    } 
private: 
    Log() : _logs(0), _log(LOG_DEFAULT_TEXT) {}; 

    std::string _log; 
    int _logs; 
}; 

Таким образом, вы можете использовать его в другом месте только доступ к

Log::instance().add("Blah Blah",true); 
+0

И если более гибкий подход необходим, что бы т он [Service Locator] (http://gameprogrammingpatterns.com/service-locator.html). – Quentin

+0

@Quentin Это звучит немного над спроектированным для такого простого использования, как ведение журнала. –

+0

Вы возвращаете локальную переменную в качестве ссылки, она не будет работать хорошо;) –

2

Другой подход к решению требования ...

Используйте функции в подходящем namespace вместо одноэлементного.

Log.h:

namespace Log 
{ 
    void add(const char *str, bool nl=true); 
    void clear(); 

    const char* get(); 
    int getNumLogs(); 
} 

Log.cpp:

namespace detail 
{ 
    // This is almost same as the class from OP's post. 
    struct LogImpl 
    { 
     LogImpl(std::string const& log) : _logs(0), _log(log) {}; 

     void add(const char *str, bool nl=true); 
     void clear(); 

     const char* get() const { return _log.c_str(); } 
     int getNumLogs() const { return _logs; } 


     std::string _log; 
     int _logs; 
    }; 
} 

namespace Log 
{ 
    // This mimics the singleton implementation... 
    // Create only one instance of LogImpl. 

    static detail::LogImpl impl("<LOG>\n\n"); 

    void add(const char *str, bool nl) 
    { 
     impl.add(str, nl); 
    } 
    void clear() 
    { 
     impl.clear(); 
    } 

    const char* get() 
    { 
     return impl.get(); 
    } 
    int getNumLogs() 
    { 
     return impl.getNumLogs(); 
    } 
} 

namespace Log 
{ 

    void add(const char *str, bool nl=true); 
    void clear(); 

    const char* get(); 
    int getNumLogs(); 
} 

Использование функции в namespace против использования синглтона

Для поддержки использования функций в пространстве имен требуется бит больше кода.

Однако он упрощает код вызова.

телефонный код можно использовать

Log::add(...); 

вместо

Log::instance()->add(...); 
1

Другим распространенным узором, унаследованные от C будет глобальным объектом. Глобалы, как правило, нахмурились, потому что они являются симптомом плохого дизайна. Но логик имеет смысл быть глобальным.

Это означает, что заголовок для вашего класса должен содержать:

extern Log log; 

и СРР

#include <log.h> 
... 
Log log; // unique definition for the global Log 

Тогда в любом модуле с помощью журнала:

#include <log.h> 
... 
log.add(...); 

Предостережение что будет статическая инициализация Log log. И C++ не всегда очень хорош с порядком статической инициализации: ... В противном случае инициализация переменной неопределенно секвенирована относительно инициализации переменной, определенной в другой единицы перевода.. Единственный надежный способ обеспечить, чтобы:

  • строительство Вход не зависит от какой-либо другой статически инициализируется переменная нуждающегося в фазе конструктора в другой единице трансляции - она ​​включает в себя std::string, но, надеюсь, не const char *
  • используется глобальная переменная один раз до того, как многопоточность начинает

Если сомневается, используйте одноплодный шаблон, предложенный πάντα ῥεῖ

+1

При использовании глобальных переменных следует указать одно значение: если порядок построения глобальных объектов не определен, вы можете столкнуться с проблемами. Возможно, создаются еще и глобальные объекты, и попытайтесь войти в систему, но регистратор недоступен, система выходит из строя без журнала;) Но наличие синглетов в деревьях зависимостей и так далее. Ни один образец не идеален. Если вы знаете, что делаете, глобальные объекты дешевы и быстры. Но если вы этого не сделаете, это начало кошмара! – Klaus

Смежные вопросы