Мой синглтон можно вызвать несколько раз - PullRequest
30 голосов
/ 02 апреля 2019

Я реализовал синглтон на основе c ++ 11. Однако в некоторых случаях конструктор может вызываться несколько раз.

Класс будет скомпилирован в статическую lib и использован другими, так что lib (более одногоLib).А система представляет собой многопоточную систему (работает на уровне Android HAL)

/// Файл .h:

class Logger
{
public:

    /// Return the singleton instance of Logger
    static Logger& GetInstance() {
        static Logger s_loggerSingleton;
        return s_loggerSingleton;
    }

private:

    /// Constructor
    Logger();
    /// Destructor
    ~Logger();
}

/// .cpp файл

Logger::Logger()
{
   ALOGE("OfflineLogger create");
}

Logger::~Logger()
{

}

Он должен быть создан один раз, например:

03-21 01:52:20.785   728  4522 E         : OfflineLogger create

Однако я вижу, что он был создан несколько раз

03-21 01:52:20.785   728  4522 E         : OfflineLogger create
03-21 01:52:20.863   728  2274 E         : OfflineLogger create
03-21 01:52:20.977   728  2273 E         : OfflineLogger create
03-21 01:52:26.370   728  4522 E         : OfflineLogger create

Вопросы:

  1. Что-то не так с моим синглтон-дизайном?Это потокобезопасная проблема?

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

Ответы [ 5 ]

28 голосов
/ 02 апреля 2019
  1. Что-то не так с моим синглтон-дизайном?Это потокобезопасная проблема?

Нет.Инициализация функции локальные static переменные гарантированно является поточно-ориентированной по стандарту.

Похоже, что мой синглтон отлично работает в одной области, но каждая такая библиотека, которая включает в себя мой синглтон, будет создавать свой собственный синглтон, так что мой синглтон больше не будет "быть синглтоном".Является ли проблема, вызванная каждой динамической связью с новым, так что «staic veriable» становится «local static»?Является ли это возможным?Если это так, как исправить

Это правильный вывод.

Вместо создания статической библиотеки, содержащей реализацию синглтона, сделайте ее динамической библиотекой.

4 голосов
/ 02 апреля 2019

Синглеты сложны, особенно с общими библиотеками.

Каждая из ваших общих библиотек имеет независимую копию не общей библиотеки.Без особой осторожности у каждого будет копия синглтона.

Чтобы иметь нетривиальные синглтоны, мне нужно было

  1. Создать чрезвычайнонизкоуровневая библиотека для помощи синглетами - назовите ее LibSingleton

  2. Создайте одноэлементный шаблон, который знает тип синглтона.Он использует магическую статику для отправки запроса в LibSingleton с размером, ключом typeid(T).name() и стертым кодом построения и уничтожения кода.LibSingleton возвращает объект RAII для подсчета ссылок.

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

  4. Когда последний дескриптор с подсчетом ссылок на данные LibSingleton исчезает, LibSingleton запускает код уничтожения и очищает память в своемнеупорядоченная карта.

Это позволяет использовать действительно простые синглтоны практически везде.

template<class T>
class singleton {
public:
  static T& Instance() {
    static auto smart_ptr = LibSingleton::RequestInstance(
      typeid(T).name(),
      sizeof(T),
      [](void* ptr){ return ::new( ptr ) T{}; },
      [](void* ptr){ static_cast<T*>(ptr)->~T(); }
    );
    if (!smart_ptr)
      exit(-1); // or throw something
    return *static_cast<T*>(smart_ptr.get());
  }
protected:
  singleton() = default;
  ~singleton() = default;
private:
  singleton(singleton&&) = delete;
  singleton& operator=(singleton&&) = delete;
};

использование выглядит следующим образом:

struct Logger : LibSingleton::singleton<Logger> {
  friend class LibSingleton::singleton<Logger>;
  void do_log( char const* sting ) {}
private:
  Logger() { /* ... */ }
};
2 голосов
/ 02 апреля 2019

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

0 голосов
/ 02 апреля 2019

статическая переменная должна быть перемещена в файл .cpp.

Простой способ - сохранить только объявление getInstance () в .h и переместить реализацию в файл .cpp.

0 голосов
/ 02 апреля 2019

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

В зависимости от вашего компилятора C ++, вы можете просто добавить #pragma once в качестве первой строки в вашем файле, например,

#pragma once
class Logger
{
    public:

    /// Return the singleton instance of Logger
    static Logger& GetInstance() {
    static Logger s_loggerSingleton;
    return s_loggerSingleton;
}

private:

    /// Constructor
    Logger();
    /// Destructor
    ~Logger();
}

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

#ifndef LOGGER_H
#define LOGGER_H
class Logger
{
    public:

    /// Return the singleton instance of Logger
    static Logger& GetInstance() {
    static Logger s_loggerSingleton;
    return s_loggerSingleton;
}

private:

    /// Constructor
    Logger();
    /// Destructor
    ~Logger();
}
#endif LOGGER_H
...