Вызов виртуальной функции для всех классов, наследуемых от данного суперкласса - PullRequest
0 голосов
/ 05 июня 2018

Я пытаюсь реализовать пользовательские настройки по умолчанию в моем приложении C ++.Для этого я создал интерфейсный класс с одной функцией:

class IRegisterUserDefaults
{
public:
    IRegisterUserDefaults();
    virtual void registerUserDefaults(){}
};

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

Пока проблем нет.Но как лучше это назвать?Я пришел из Objective-C, где я мог просто найти все классы и найти тех, кто реализует интерфейс, и вызвать для них функцию registerUserDefaults.Я понимаю, однако, что C ++ не имеет такого уровня самоанализа.Было бы достаточно вызвать функцию один раз для каждого класса (и, следовательно, сделать ее статической).

Objective

Было бы здорово, если бы функция вызывалась "автоматически"msgstr "если класс является подклассом IRegisterUserDefaults.Я попытался вызвать метод из конструктора IRegisterUserDefaults, но похоже, что он не вызывает функцию подкласса должным образом.Есть ли способ сделать это возможным?

Кроме того, как лучше всего убедиться, что это вызывается только один раз для класса?

Ответы [ 4 ]

0 голосов
/ 05 июня 2018

Есть ли у вас единственное место, где все эти производные классы известны?В таком случае, сделайте это там:

// The type-list can be used in many ways, as you need
using all_classes = std::tuple<A, B, C, D /* and so on */>;

template <class... Ts>
static void register_all_classes(Y<Ts...>*)
{ ((Ts().registerUserDefaults()), ...); }

register_all_classes((all_classes*)nullptr);

В противном случае вы, очевидно, должны перейти на децентрализацию:

Есть ли у вас единица компиляции, отвечающая за регистрацию каждого класса?В этом случае используйте объект области имен.Возможно, используйте для этого помощника:

template <class T>
struct Init {
    Init() { T().registerUserDefaults(); }
};

// Used in single TU as:
static Init<SomeRegisterUserDefaults> _;

В противном случае посмотрите на std::ios_base::Init как это делает <iostream>.Я упростила, потому что не было необходимости в значении Uninit:

template <class T>
struct MultiInit {
    MultiInit() { static Init<T> _; }
};

// Used in any number of TUs as:
static MultiInit<SomeRegisterUserDefaults> _;
0 голосов
/ 05 июня 2018

Это работает для вас?

#include <iostream>
#include <string>

class IRegisterUserDefaults
{
public:
    IRegisterUserDefaults() {}
    virtual void registerUserDefaults() = 0;
};

class MoreDerivedRegisterUserDefaults : public IRegisterUserDefaults
{
public:
    MoreDerivedRegisterUserDefaults (int x, int y) : m_x (x), m_y (y) { }
    virtual void registerUserDefaults() override {
        std::cout << "MoreDerivedRegisterUserDefaults::registerUserDefaults called (" << m_x << ", " << m_y << ")" << std::endl;
    }
private:
    int m_x, m_y;
};

template <class T, typename... Args> void RegisterDefaultsHandler (Args... args) {
    T obj (args...);
    obj.registerUserDefaults ();
}    

int main ()
{
    RegisterDefaultsHandler<DerivedRegisterUserDefaults> ();
    RegisterDefaultsHandler<MoreDerivedRegisterUserDefaults> (1, 2);
    // ...
}

Вы должны создать экземпляр каждого производного класса где-то .

Live demo (обновлено),Вывод:

DerivedRegisterUserDefaults::registerUserDefaults called
MoreDerivedRegisterUserDefaults::registerUserDefaults called (1, 2)

РЕДАКТИРОВАТЬ: Поговорив с @Caleth, я немного подправил код, чтобы прояснить свои намерения.

РЕДАКТИРОВАТЬ 2: Добавлен шаблон Variadiac, который оказался проще, чем я думал, полезное руководство "Howto" здесь .

0 голосов
/ 05 июня 2018

IRegisterUserDefaults не является значимым интерфейсом, на любом языке.

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

class HasUserDefaults {
    static std::once_flag register_once;
    void registerUserDefaults() { /*...*/ }
public:
    HasUserDefaults ()
    { 
      // in all the constructors
        std::call_once(register_once, &HasUserDefaults::registerUserDefaults, this);
    }

    // other members
};
0 голосов
/ 05 июня 2018

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

...