2015-06-26 1 views
0

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

class MSys : public QObject 
{ 
    Q_OBJECT 
public: 
    explicit MSys(QObject *parent = 0) : QObject(parent) {} 
    virtual ~MSys() {} 

public slots: 
    void onRequset(); 
}; 

class AbsView 
{ 
protected: 
    AbsView() : m_sys(new MSys) 
    { 
    // QObject::connect(this, SIGNAL(request()), m_sys, SLOT(onRequset())); 
    /* What can I do here !? */ 
    } 

public: 
    virtual ~AbsView() {} 

signals: 
    virtual void request() = 0; 

private: 
    MSys *m_sys; 
}; 

Q_DECLARE_INTERFACE(AbsView, "AbsView") 

Это не проблема, когда конструктор закончил: то dynamic_cast<QObject*>(this) будет работать в любой из методов интерфейса. Но внутри конструктора это кажется невозможным.

Есть ли способ сделать это?

+0

Какую ошибку вы получаете? –

+0

@MatthewRead Спасибо за вопрос. 'this' в' QObject :: connect' не является QObject. – SaMax

+0

О, ну, 'AbsView' нужно будет расширить' QObject'. –

ответ

1

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

Если вы настаиваете на том, чтобы делать такие вещи, семантика C++ мешает нам делать это точно так, как вы заявляете. Когда вы умножаете наследование, dynamic_cast к C не удастся, пока конструктор C не вводится:

struct Interface { 
    Interface() { assert(dynamic_cast<QObject*>(this) == 0); } 
}; 

struct C : public QObject, public Interface { 
    C() { assert(dynamic_cast<QObject*>(this)); } 
}; 

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

Простой способ будет для интерфейса явно требуется база будет построена:

struct Interface { 
    Interface(QObject * base) { 
    connect(base, ...); 
    } 
}; 

struct C : public QObject, public Interface { 
    C() : Interface(this) {} 
}; 

Конструктор подписи красиво выражает намерение: Interface предназначается для использования на классах, вытекающих из QObject. Это не будет работать на тех, кто этого не делает.

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

Таймер принадлежит диспетчеру событий для текущего потока, поэтому он не будет течь, даже если цикл событий не запускается.

class Interface { 
public: 
    virtual void request() = 0; // entirely optional 
    Interface() { 
    auto timer = new QTimer(QAbstractEventDispatcher::instance()); 
    timer.start(0); 
    QObject::connect(timer, &QTimer::timeout, [this, timer]{ 
     timer.deleteLater(); 
     connect(dynamic_cast<QObject*>(this), SIGNAL(request()), ...); 
    });  
    } 
}; 

Обратите внимание, что во всех случаях совершенно бесполезно, чтобы сигнал был объявлен виртуальным в интерфейсе. Конструктор Interface может проверить, присутствует ли сигнал, и утверждать его, или даже всегда abort().

Просто объявляя виртуальный сигнал, не гарантирует, что сигнал на самом деле является сигналом.Ниже не будет работать, даже если компилятор не обеспечивает диагностику наоборот:

struct Interface { 
    signals: 
    virtual void aSignal() = 0; 
}; 

struct Implementation : public QObject, public Interface { 
    void aSignal() {} // not really a signal! 
}; 

Это обнаружит отсутствующий сигнал на время выполнения:

struct Interface { 
    // No need for virtual signal! 
    Interface(QObject * base) { 
    Q_ASSERT(base->metaObject()->indexOfSignal("request()") != -1); 
    } 
}; 

struct Implementation : public QObject, public Interface { 
    Q_OBJECT 
    Q_SIGNAL void reuest(); // a typo 
    Implementation() : Interface(this) {} // will assert! 
}; 

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

struct Interface { 
    virtual void aSignal() = 0; 
    Interface(QObject * base) { 
    Q_ASSERT(base->metaObject()->indexOfSignal("request()") != -1); 
    } 
}; 

struct Implementation : public QObject, public Interface { 
    Q_OBJECT 
    Q_SIGNAL void request() Q_DECL_OVERRIDE; 
    Implementation() : Interface(this) {} 
}; 

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

Следует также отметить, что раздел signals:Interface является фиктивным.

  1. signals препроцессора макрос с пустым расширением: компилятор, он ничего не делает.

  2. Interface не является QObject и поэтому игнорируется moc.

  3. signals: имеет смысл только тогда и только тогда MOC это в классе, который как:

    • производный от QObject и
    • содержит Q_OBJECT макрос.

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

Наконец, они не работают с Qt 5 во время компиляции проверяется синтаксис либо, так как Interface не конкретный QObject -deriving класс с соответствующим сигналом

+0

На самом деле часть «задержки связи» уже была в моем сознании. Ваше первое решение - это то, что я сделал, но думал о лучшем способе. Вот почему я задал этот вопрос. В любом случае, поскольку ответ альтернативно правильный, я отмечаю его как правильный ответ. +1 для детальной записи. Большое спасибо. – SaMax

+0

Благодарим за редактирование моего вопроса. – SaMax