Я пытаюсь реализовать пользовательские настройки по умолчанию в моем приложении C ++.
Для этого я создал интерфейсный класс с одной функцией:
class IRegisterUserDefaults
{
public:
IRegisterUserDefaults();
virtual void registerUserDefaults(){}
};
Каждый класс, унаследованный от этого класса интерфейса, будет реализовывать функцию для регистрации пользовательских значений по умолчанию, которые он должен установить.
Пока проблем нет. Но как лучше это назвать?
Я пришел из Objective-C, где я мог просто найти все классы и найти тех, кто реализует интерфейс, и вызвать для них функцию registerUserDefaults. Я понимаю, однако, что C ++ не имеет такого уровня самоанализа. Было бы достаточно вызвать функцию один раз для каждого класса (и, таким образом, сделать ее статической).
Задача
Было бы замечательно, если бы функция вызывалась «автоматически», если бы подклассы класса IRegisterUserDefaults. Я попытался вызвать метод из конструктора IRegisterUserDefaults, но похоже, что он не вызывает функцию подкласса должным образом. Есть ли способ сделать это?
Кроме того, что было бы лучшим способом удостовериться, что это вызывается только один раз для класса?
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
};
У вас есть единственное место, где все эти производные классы известны? В этом случае сделайте это там:
// 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> _;
Это работает для вас?
#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);
// ...
}
Вы должны создать экземпляр каждого производного класса где-то.
Жить демонстрация (Обновлено). Выход:
DerivedRegisterUserDefaults::registerUserDefaults called
MoreDerivedRegisterUserDefaults::registerUserDefaults called (1, 2)
РЕДАКТИРОВАТЬ: Поговорив с @Caleth, я немного подправил код, чтобы прояснить свои намерения.
РЕДАКТИРОВАТЬ 2: Добавлен шаблон Variadiac, который оказался проще, чем я думал, полезное руководство по «Howto» Вот.
Вызовите метод в конструкторе подкласса, вы не можете вызвать его в конструкторе базового класса, так как подкласс еще не создан.