Множественное наследование QObject из-за доступа к методу deleteLater()

Я использую наблюдаемый наблюдателем шаблон в своей программе. Все работало до того, как мне пришлось немного изменить код. Если быть точным, я изменил наследование класса IObserver - теперь он наследует QObject:

class IObserver : public QObject
{
...

Я сделал это только из-за одного - мне нужно, чтобы метод deleteLater() использовался в наблюдателе, чтобы я мог вызвать реализацию виртуальной функции deinitialization() IObserver. Таким образом, я мог стандартизировать каждый обработчик сообщений IObserver.

Проблема в том, что я уже унаследовал QObject (косвенно) в некоторых классах Observer. Например, MainForm или AboutDialog. Все идет нормально, пока я не попытаюсь вызвать метод «connect» в классе AboutDialog.

Что я могу сделать? Мне действительно нужен этот метод deleteLater(), поскольку я не могу использовать «удалить это» в коде IObserver — это вызовет деструктор IObserver, а не, например, классы MainForm или Storage.

Спасибо.


person Alex Tiger    schedule 11.12.2013    source источник
comment
может быть, здесь есть подсказка: http://stackoverflow.com/questions/3259728/using-qt-signals-and-slots-with-multiple-inheritance   -  person epsilon    schedule 11.12.2013


Ответы (2)


Мне действительно нужен этот метод deleteLater(), поскольку я не могу использовать «удалить это» в коде IObserver — это вызовет деструктор IObserver, а не, например, классы MainForm или Storage.

Если вы сделаете свой деструктор виртуальным (а вы должны!) он будет нормально вызывать производный деструктор. Но проблема в том, что разрушение объекта во время обработки им какого-то сигнала/слота может вызвать проблемы с циклом обработки событий. В любом случае с delete this нужно быть очень осторожным.

Проблема в том, что я уже унаследовал QObject (косвенно) в некоторых классах Observer.

Один из способов реализовать это, не уверен, что это лучшая мысль:

template <typename Derived>
class IObserver
{

    // Just to be sure: (C++11)
    static_assert(is_base_of<Derived, QObject>::value,
                  "must inherit from QObject when using IObserver");

    void deleteMe()
    {
        QObject* thisObject = dynamic_cast<QObject*>(this);
        // no need for check if thisObject equals null. static assert does this.
        thisObject->deleteLater();
    }

};

class MainForm : public IObserver<MainForm>, public QMainWindow
{
    // ...
};

Я считаю, что этот шаблон называется статическим полиморфизмом.

person Guilherme Bernal    schedule 11.12.2013
comment
Если вы сделаете свой деструктор виртуальным (а вы должны!) Я использую virtual void deinitialisation() = 0 в IObserver и считаю, что в реализованной версии все ресурсы будут освобождены, поэтому после вызова deinit я хочу удалить объект. Спасибо за пример, завтра попробую. - person Alex Tiger; 11.12.2013
comment
Проблема в том, что если кто-то решит delete anIObserverPointer, это не очистит ресурсы производного класса. Кроме того, почему бы не переместить код с deinitialization() на ~IObserver()? Гораздо безопаснее. - person Guilherme Bernal; 11.12.2013

Отказаться от наследования QObject для IObserver. Вместо этого добавьте такой метод в интерфейс.

class IObserver : public QObject {
public:
   QObject *object() const = 0;
...

Затем, если реализация интерфейса наследует QObject, вы вернете указатель this из метода object(). Если реализация интерфейса не наследует QObject, вы можете просто вернуть указатель на какой-нибудь простой QObject, который будет обрабатывать уничтожение этого объекта. Затем вы можете просто подключить deleteLater для объекта, возвращаемого этим методом.

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

person Marek R    schedule 11.12.2013
comment
›работает отлично Не во всех случаях. Когда нужно, чтобы каждый возможный сигнал был записан в журнал, необходимо подключить эти сигналы к функциям журнала, что не стандартизировано. Вместо создания 20-30 промежуточных функций я использую универсальную функцию с 3 параметрами (та же идея, что и в Windows SendMessage). Кроме того, я могу полностью удалить обозревателей, не повредив приложение. Вместо вызова конкретной функции, например, profileManager.getProfilesList, я вызываю Send(OC_PROFILEMNGR, MC_GETPRFNAMES, &myStringList). Поэтому мне все равно, есть ли какая-либо функция, такая как getProfileList, или даже существует ли такой менеджер. - person Alex Tiger; 11.12.2013