Как правильно проверить, был ли создан экземпляр синглтона или нет? - PullRequest
0 голосов
/ 03 мая 2018

Я видел следующие два примера:

class singleton {
    protected:
        static singleton *instance;
        singleton() { }
    public:
        static singleton *getInstance() {
            if (instance == 0)
                instance = new singleton();
            return instance;
        }
};
singleton *singleton::instance = 0; //This seems weird - why isn't nullptr being used ?

И этот пример:

class Singleton
{
  private:

    static Singleton *p_inst;
    Singleton();

  public:

    static Singleton * instance()
    {
      if (!p_inst) // why isn't this being compared with nullptr ?
      {
        p_inst = new Singleton();
      }

      return p_inst;
    }
};

Чтобы проверить, был ли создан экземпляр, почему люди не делают что-то подобное:

class Singleton
{
  private:

    static Singleton *p_inst = nullptr;
    Singleton();

  public:

    static Singleton * instance()
    {
      if (p_inst != nullptr) // why isn't this being compared with nullptr ?
      {
        p_inst = new Singleton();
      }

      return p_inst;
    }
};

Какой правильный путь?

РЕДАКТИРОВАТЬ: После всех ответов ниже, я действительно смущен. Каков окончательный способ создания поточно-ориентированного синглтона без каких-либо ошибок.

1 Ответ

0 голосов
/ 03 мая 2018

Обратите внимание, что ни один из перечисленных вами примеров на самом деле не пытается удалить синглтон, и ни один из них не является поточно-ориентированным, поскольку они ничего не делают для синхронизации своих:

  • сравнить с нулевым (разных вкусов)
  • их размещение объекта
  • их назначение указателя

Ни один из ваших существующих методов не может безопасно проверить, был ли синглтон еще создан.

C ++ 11 добавляет гарантию для члена статических данных метода, что его инициализация будет поточно-ориентированной - то есть вызываться только один раз. Это рекомендуемый механизм. Классический синглтон - это что-то вроде:

SClass& Singleton()
{ 
  static SClass single(args);
  return single;
}

Если вы действительно хотите, чтобы объект был выделен в куче:

SClass* Singleton()
{ 
  static std::unique_ptr<SClass> single (new SClass(args));
  return single;
}

[Обратите внимание, что вы можете использовать вызовы функций и / или лямбды в конструкции, если конструкция вашего объекта не является простой новой. Вы можете написать лямбду как второй аргумент std :: unique_ptr, чтобы получить сложное уничтожение, так что вы можете легко настроить ее внутри этой модели. ]

Если вы действительно хотите сделать это без использования статического члена, вы можете использовать std :: mutex или std :: atomic с cmpexchange, но в c ++ 11 статический член работает для вас.

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

Версии, которые вы просматриваете, просто никогда не удаляют объект, что сделает valgrind очень несчастным. Если вам нужно ваше существующее поведение с поточно-безопасной конструкцией, но без уничтожения, используйте необработанный указатель (у них нет деструкторов):

SClass* Singleton()
{ 
  static SClass* single = new SClass(args);
  return single;
}

Обратное исправление заключается в том, чтобы SomeThing вызывал синглтон во время его создания или мог обрабатывать nullptr по возвращении. Конечно, вы должны отладить этот сценарий, прежде чем вы сможете это исправить. Поэтому я рекомендую вам, по крайней мере, добавить аварийный сигнал в ваш синглтон, чтобы вы знали, почему он не работает: [На самом деле, как отмечено в комментариях, это не так тривиально, так как unique_ptr может не оставить себя в хорошем состоянии после уничтожения, поэтому нам нужен собственный обработчик времени жизни ptr. К счастью, нам не нужен полноценный unique_ptr, всего 3 или 4 метода. Потенциально это входит в UB, так как компилятор может оптимизировать это нулевое назначение во время уничтожения, или DEADBEEF это местоположение, но фениксы в любом случае не должны использоваться в производственном коде.]

SClass* Singleton()
{ 
  struct PUP: std::unique_ptr<SClass> single
  {
    typedef std::unique_ptr<SClass> base;
    PUP(SClass* s): base(s) {}
    ~PUP()  { operator =( base()_); }
  };
  static PUP single (new SClass(args));
  assert(single && "Singleton has been called in late static destructor - need phoenix");
  return single;
}

Вы можете, если хотите, поднять объект из мертвых. Это должно быть безопасно, поскольку статическое разрушение является однопоточным. Обратите внимание, что повторно возникший объект никогда не может быть уничтожен и не сохраняет состояния старого объекта. Это может быть полезно для регистрации классов, когда кто-то добавляет запись в деструкторе, не понимая, что синглтон класса регистрации уже уничтожен. Для диагностики это полезно. Что касается производственного кода, он все равно расстроит valgrind, просто не делайте этого!

SClass* Singleton()
{ 
  static PUP single = new SClass(args);
  if (!single)
  {
    single = std::unique_ptr<SClass>(new SClass(args));
    // Hmm, and here is another phoenix being revived:
    Logger(CRITICAL) << " Revived singleton for class SClass!" << std::endl;
  }
  return single;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...