2010-11-25 2 views
2

У меня есть объект-планировщик class-. В этом классе планировщика указываются указатели на функции, время и указатель на объект, который создал планировщик.
Это означает, что я мог бы сделать что-то такое: (pObject->*h.function)(*h.param); Где pObject - это указатель на исходный объект, h класс, который содержит указатель function + void parameter, поэтому я могу передать аргументы исходной функции.
Когда мне нравится инициализировать этот объект, у меня есть конструктор explicit Scheduler(pObjType o); (где pObjType является параметром шаблона).Получить (указатель на) вызывающий объект

Когда я создаю объект, который должен иметь этот сигнал тревоги I типа:

struct A { 
    typedef void (A::*A_FN2)(void*); 
    typedef Scheduler<A*,A_FN2> AlarmType; 
    A(int _x) : alarm(NULL) 
    { 
     alarm.SetObject(this); 
    } 
    AlarmType alarm 

Однако этот сигнал типа ставит достаточно большое ограничение на объекте: , если я забыл добавить копию-конструктор (для A) класс получит неопределенное поведение. Планировщик будет указывать на исходный объект, и этот исходный объект может выйти из сферы действия или, что еще хуже, может и не быть.

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

ответ

1

Как я вижу, у вас есть возможность улучшить свой дизайн здесь, что может помочь вам избавиться от вашего беспокойства.

  1. Как правило, это плохая идея передать вокруг указателей функций членов. Это лучше сделать ваши структуры наследовать от абстрактного базового класса, делать функции, которые вы хотите настроить абстрактные виртуальные.
  2. Если вам не нужно копировать, лучше всего запретить его в базовом классе. Либо сделав конструктор и оператор копирования неопределенным и приватным, или наследуя boost::NonCopyable.
+0

Проблема с 1. заключается в том, что это * функции-члены * в настоящее время. Но позже я хочу расширить его и, возможно, дать дескрипторы (LUA) скриптов, чтобы они могли быть настроены на уровне дизайна вместо уровня двигателя. Проблема с 2 заключается в том, что я действительно НЕОБХОДИМО копировать, но поскольку нет другого требования к конструктору-конструктору, отличному от используемого по умолчанию (я разработал все, чтобы это не было необходимо), я надеялся предотвратить необходимость здесь. – paul23 2010-11-25 12:07:10

0

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

Другое дело, что вы действительно должны использовать boost ::/std :: function <>, они гораздо более общие, и вам это понадобится, если вы хотите использовать функции Lua.

0

Самый простой способ предотвратить конкретную проблему, о которой вы просите, состоит в том, чтобы сделать Scheduler noncopyable (например, с помощью boost::noncopyable). Это означает, что любой класс клиента, включающий элемент значения типа Scheduler, не сможет быть скопирован. Надежда состоит в том, что это дает подсказку для программиста проверить документы и выяснить семантику копирования Scheduler (т. Е. Построить новый Scheduler для каждого нового A), но для кого-то это может быть ошибкой, если они справляются с проблемой просто удерживая указатель Scheduler. Смещение указателя дает точно такую ​​же проблему, как построение по умолчанию-копирование экземпляра Scheduler, который содержит указатель.

Каждый раз, когда у вас есть необработанные указатели, вы должны иметь политику в отношении срока жизни объекта. Вы хотите, чтобы убедиться, что срок службы любого класса A, по крайней мере до тех пор, как соответствующий экземпляр Scheduler, и как я вижу три способа обеспечить это:

  • Использование композиции - не представляется возможным в данном случае потому что A содержит Scheduler, поэтому Scheduler не может содержать A
  • Использовать наследование, например в виде шаблона Curiously Recurring Template (CRTP)
  • Имейте глобальную политику по обеспечению времени жизни экземпляров A, например. требуя, чтобы они всегда проходят по смарт-указатель, или что очистка их несет ответственность за какой-то класс, который также знает, чтобы очистить Scheduler S, которые зависят от них

CRTP может работать так:

#include <iostream> 

using namespace std; 

template<typename T> 
struct Scheduler { 
    typedef void (T::* MemFuncPtr)(void); 

Scheduler(MemFuncPtr action) : 
    action(action) 
    { 
    } 

    private: 
    void doAction() 
    { 
     this->*action(); 
    } 

    MemFuncPtr action; 
}; 

struct Alarm : private Scheduler<Alarm> { 
    Alarm() : Scheduler<Alarm>(&Alarm::doStuff) 
    { 
    } 

    void doStuff() 
    { 
     cout << "Doing stuff" << endl; 
    } 
}; 

Обратите внимание, что частное наследование гарантирует, что клиенты класса Alarm не могут обрабатывать его как необработанный Scheduler.

Смежные вопросы