Вложенный класс или не вложенный класс?
-
19-09-2019 - |
Вопрос
У меня есть класс A и список объектов A.A имеет функцию f, которая должна выполняться каждые X секунд (для первого экземпляра каждую 1 секунду, для второго экземпляра каждые 5 секунд и т. д.).У меня есть класс планировщика, который отвечает за выполнение функций в нужное время.Я решил создать новый класс ATime, который будет содержать ptr для экземпляра A и время выполнения A::f.Планировщик будет удерживать очередь Atime с минимальным приоритетом.
- Как вы думаете, это правильная реализация?
- Должен ли ATime быть вложенным классом планировщика?
Решение
Судя по тому, что вы описываете, это может сработать :-)
ИМХО класс ATime
принадлежит планировщику больше, чем A.Это необходимо планировщику для выполнения своей работы, и оно не нужно для А.На самом деле, A (и остальному миру) даже не обязательно знать о его существовании — поэтому для меня было бы уместно поместить его в частный вложенный класс планировщика.
Другие советы
Возможно, это слишком продвинуто, но вот...
повышение::функция и повышение::связывание может использоваться для реализации планировщика, которому не нужно ничего знать о классе A.Это сделает ваш планировщик более универсальным и пригодным для повторного использования.
Вот пример кода, который иллюстрирует, как эти возможности Boost можно использовать в вашем случае:
#include <ctime>
#include <queue>
#include <boost/function.hpp>
#include <boost/bind.hpp>
struct Foo
{
void onScheduler(time_t time) {/*...*/}
};
struct Bar
{
void onScheduler(time_t time) {/*...*/}
};
typedef boost::function<void (time_t)> SchedulerHandler;
struct SchedulerEvent
{
bool operator<(const SchedulerEvent& rhs) const {return when < rhs.when;}
SchedulerHandler handler;
time_t when;
};
class Scheduler
{
public:
void schedule(SchedulerHandler handler, time_t when)
{
SchedulerEvent event = {handler, when};
queue_.push(event);
}
private:
std::priority_queue<SchedulerEvent> queue_;
void onNextEvent()
{
const SchedulerEvent& next = queue_.top();
next.handler(next.when);
queue_.pop();
}
};
int main()
{
Scheduler s;
Foo f1, f2;
Bar b1, b2;
time_t now = time(0);
s.schedule(boost::bind(&Foo::onScheduler, &f1, _1), now + 1);
s.schedule(boost::bind(&Foo::onScheduler, &f2, _1), now + 2);
s.schedule(boost::bind(&Bar::onScheduler, &b1, _1), now + 3);
s.schedule(boost::bind(&Bar::onScheduler, &b2, _1), now + 4);
// Do scheduling...
return 0;
}
Обратите внимание, что Scheduler
ничего не знает о Foo
& Bar
, и наоборот.Все Scheduler
действительно хочет — это функтор «обратного вызова», который соответствует сигнатуре, указанной SchedulerHandler
.
Если вам нужен SchedulerEvent
быть отмененным, все становится немного сложнее, потому что boost::function
объекты несопоставимы.Чтобы обойти эту проблему, вам нужно будет вернуть какой-то токен «соединения» при регистрации событий.По сути, это то, что делает Boost.Signal.
Надеюсь это поможет.
Этот тип класса лучше всего сделать частной структурой, вложенной в ваш класс планировщика, чтобы вы могли легко получить доступ ко всем полям внутри класса планировщика.(Все поля структуры по умолчанию являются общедоступными.)
Что касается второй части (и вопроса в заголовке), то, по моему мнению, это полностью дело вкуса.С тем же успехом вы могли бы уменьшить беспорядок в определении класса Scheduler и поместить ATime в подпространство имен с именем предупреждения (например, detail
), чтобы люди не использовали его.В конце концов, если это полезно только для Планировщика, нет необходимости слишком сильно его скрывать — все равно никто не захочет его использовать.
Возможно, в C++0x все может быть по-другому (мне кажется, я слышал некоторые слухи о том, как будут изменены правила доступности между вложенным и родительским классом или около того, и в этом случае вложение может быть более целесообразным).
Для универсальности вы также можете использовать шаблоны и, возможно, в конечном итоге получить Scheduler<A>
(используя TimedOperation<A>
) (или множество потенциальных усовершенствований/обобщений этого)?