Синглтон: избегайте GetIntance () - PullRequest
0 голосов
/ 11 ноября 2018

Всем известен шаблон singleton (очень простой пример):

class Singleton
{
private:
    static Singleton* instance;

public:
    static Singleton* GetInstance() { return instance; }

    void Foo() {}
};

И использование:

Singleton::GetInstance()->Foo();

Интересно, почему это GetInstance() обязательно? Я попытался найти способ исключить вызов этой функции, чтобы иметь возможность писать для примера ...:

Singleton->Foo();

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

Каждый раз правило C ++ запрещает следующие примеры:

Singleton->Foo(); // (1)
Singleton()->Foo(); // (2)
Singleton.Foo(); // (3)
Singleton::Foo(); // (4)

Потому что:

  • (1) static Singleton* operator->() { return instance; } невозможно, перегрузка оператора доступа к элементу класса (->) не может быть статической (см. C2801 ), то же самое для (2) с оператором ()
  • (3) и (4) операторы. и :: не может быть перегружен

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

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

Я не ожидаю решения (но если оно есть, оно было бы замечательно), это просто для понимания, почему, чтобы знать, есть ли объяснение, причина проектирования, причина безопасности или что-то еще!

Заранее спасибо!

Ответы [ 2 ]

0 голосов
/ 11 ноября 2018

Я могу думать о следующем пути в C++11:

#include <memory>

struct singleton
{
    int i;
};

template <class Singleton_t> 
struct singleton_holder
{
    auto* operator -> () const noexcept
    {
        static Singleton_t s;
        return std::addressof(s);
    }
};

// optional C++14 feature, can be commented if wanted
template <class Singleton_t>
constexpr const static /* inline */ singleton_holder<Singleton_t> singleton_v{};

Теперь с помощью этого кода вы можете просто создать singleton_holder<singleton> s и использовать s->i для прямого доступа к члену singleton.

Или вы можете использовать C++14 way: singleton_v<singleton>->i для прямого доступа к элементу singleton.

0 голосов
/ 11 ноября 2018

Ну, один из способов сделать это состоит в том, чтобы свести одноэлементную часть проблемы только к данным и создать статические функции, которые обращаются к этим данным через функцию «instance» (в этом примере называемую self()):

class Singletonish
{
private:
    // single (inaccessible) instance of the data
    static auto& self()
    {
        static struct {
            std::string s = "unset";
            int i = 0;
        } data;
        return data;
    }

public:

    static void foo()
    {
        // foo code here
        self().s = "hello";
        self().i = 9;
    }

    static void woo()
    {
        // woo code here
        std::cout << self().s << '\n';
        std::cout << self().i << '\n';
    }
};

int main()
{
    Singletonish::woo();

    Singletonish::foo();

    Singletonish::woo();
}

Вывод:

unset
0
hello
9

Лично я рекомендую просто сделать это обычным способом: https://stackoverflow.com/a/1008289/3807729

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