Russian Qt Forum
Ноябрь 23, 2024, 00:01 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Приведение к типу  (Прочитано 5546 раз)
wxmaper
Гость
« : Сентябрь 04, 2015, 05:57 »

Есть вот такой незамысловатый header:
Код:
#define PQ_OBJECT \
public:\
    QStringList myList;

class PQWidget;
class PQWidget : public QWidget {
    Q_OBJECT
    PQ_OBJECT

public:
    Q_INVOKABLE explicit PQWidget(QWidget *parent = 0)
        : QWidget(parent){}
    virtual ~PQWidget(){}
};

class PQPushButton;
class PQPushButton : public QPushButton {
    Q_OBJECT
    PQ_OBJECT

public:
    Q_INVOKABLE explicit PQPushButton(QWidget *parent = 0)
        : QPushButton(parent) {
        myList << "text1";
        myList << "text2";
    }
};

и код:
Код:
// QObject -> PQWidget
    QObject *button = new PQPushButton;
    PQWidget *pbutton = (PQWidget*) button;

    if(pbutton->myList.contains("text1")) {
        qDebug() << "Okay!";
    }

    qDebug() << "QObject -> PQWidget OK!";

вроде написано всё честно и справедливо - PQWidget является потомком QWidget, как и кнопка. И все они после моего наследования имеют поле myList. Код работает. И какой бы виджет я не писал с добавлением макроса PQ_OBJECT, будь то различные кнопки, текстовые поля, контейнеры и... короче, если взять абсолютно любой виджет, он прекрасно преобразуется в PQWidget и не выпендривается Улыбающийся

А вот второй похожий случай:
Код:
class PQObject;
class PQObject : public QObject {
    Q_OBJECT
    PQ_OBJECT

public:
    Q_INVOKABLE explicit PQObject(QObject *parent = 0)
        : QObject(parent){}
    virtual ~PQObject(){}
};

class PQTimer;
class PQTimer : public QTimer {
    Q_OBJECT
    PQ_OBJECT

public:
    Q_INVOKABLE explicit PQTimer(QObject *parent = 0)
        : QTimer(parent) {
        myList << "text1";
        myList << "text2";
    }
};

Код:
// QObject -> PQObject
    QObject *timer = new PQTimer;
    PQObject *ptimer = (PQObject*) timer;

    if(ptimer->myList.contains("qwerty")) { // Segmentation fault
        qDebug() << "Okay!";
    }

    qDebug() << "QObject -> PQObject OK!";

Все написано в точности так же как и с QWidget. Полная аналогия: PQObject и QTimer - потомки QObject. PQObject и PQTimer имеют поле myList. На этапе преобразования QObject -> PQObject программа работает, но при попытке обратится к какому либо полю из PQ_OBJECT - сегфолт.

Ребята, прокомментируйте, пожалуйста, такое поведение? Почему в первом случае код работает, а во втором - нет? Должен ли он вообще был работать или это случайность, которая оказалась случайной для всех виджетов? Улыбающийся

П.с. хочу заметить, что унаследоваться от PQObject , как по идее и должно было быть, я не могу, потому что Qt не допускает двойное наследование Q_OBJECT, а мой PQObject обязательно должен быть наследником QObject.

П.п.с. макрос обрезал для читаемости, на самом деле в нем намного больше полей и методов и все они доступны (проверено) при приведении к PQWidget.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #1 : Сентябрь 04, 2015, 11:07 »

Код:
    QObject *timer = new PQTimer;
    PQObject *ptimer = (PQObject*) timer;
Для PQObject
Цитировать
VMT
поля QObject
PQObject   (myList)

Для PQTimer
Цитировать
VMT
поля QObject
поля QTimer
PQObject   (myList)
Поэтому работать не должно, т.к. myList сидит по разным смещениям. Почему работает для PQPushButton - не знаю, возможно нет полей QPushButton (хранит все в "d"). В любом случае затея с PQObject (Widget) некорректна, надо использовать множественное наследование и потом кросс-приводиться
« Последнее редактирование: Сентябрь 04, 2015, 11:10 от Igors » Записан
wxmaper
Гость
« Ответ #2 : Сентябрь 04, 2015, 11:14 »

А что это за вывод вы привели? Что за VMT?
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #3 : Сентябрь 04, 2015, 11:17 »

А что это за вывод вы привели? Что за VMT?
Это формат объектов в памяти, VMT - указатель на таблицу виртуалов

Edit: есть же простой способ проверить!
Код
C++ (Qt)
qDebug() << sizeof(QWidget);
qDebug() << sizeof(QPushButton);
qDebug() << sizeof(QObject);
qDebug() << sizeof(QTimer);
 
« Последнее редактирование: Сентябрь 04, 2015, 11:20 от Igors » Записан
wxmaper
Гость
« Ответ #4 : Сентябрь 04, 2015, 11:23 »

Это формат объектов в памяти, VMT - указатель на таблицу виртуалов
Мне больше интересно как вы это сообщение получили Улыбающийся как мне самому это все увидеть? Первый раз о таком слышу ^_^

надо использовать множественное наследование

Я не могу сообразить как корректно организовать множественное наследование из-за ограничений Qt: нельзя дважды наследовать Q_OBJECT. PQObject мне нужен как обертка над QObject, PQTimer как обертка над QTimer и т.д. их много. И у всех должны быть методы или поля именно PQObject'a, поэтому пришлось выкручиваться через макросы. А PQObject должен наследоваться от QObject, иначе нет доступа к метасистеме Qt... в обещем раскрутить этот клубок у меня не получается.
Записан
Racheengel
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2679


Я работал с дискетам 5.25 :(


Просмотр профиля
« Ответ #5 : Сентябрь 04, 2015, 11:29 »

Не, множественное наследование не прокатит из-за Q_OBJECT. Надо теплейтами делать.
Записан

What is the 11 in the C++11? It’s the number of feet they glued to C++ trying to obtain a better octopus.

COVID не волк, в лес не уйдёт
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #6 : Сентябрь 04, 2015, 11:34 »

Мне больше интересно как вы это сообщение получили Улыбающийся как мне самому это все увидеть? Первый раз о таком слышу ^_^
Создайте экземпляр класса и посмотрите его в отладчике

Я не могу сообразить как корректно организовать множественное наследование из-за ограничений Qt: нельзя дважды наследовать Q_OBJECT. PQObject мне нужен как обертка над QObject, PQTimer как обертка над QTimer и т.д. их много. И у всех должны быть методы или поля именно PQObject'a, поэтому пришлось выкручиваться через макросы. А PQObject должен наследоваться от QObject, иначе нет доступа к метасистеме Qt... в обещем раскрутить этот клубок у меня не получается.
Код
C++ (Qt)
struct PQObject {    // не наследуемся от QObect
QStringList myList;
};
 
class PQTimer : public QTimer, public PQObject {
...
};
Теперь напр в какой-то ф-ции Вы имеете только PQObject
Код
C++ (Qt)
void SomeFunc( PQObject * po )
{
QObject * obj = dynamic_cast <QObect *> (po); // кросс-приведение
if (obj) {  // это множ унаследлванный po
..
}
else {  // а этот нет
...
}
}
Записан
wxmaper
Гость
« Ответ #7 : Сентябрь 04, 2015, 11:40 »

Ну это понятно! Только кроме PQTimer мне нужен еще и сам PQObject со всеми плюшками Q_OBJECT'a...

Код:
// есть не только такой код:
QWidget *button= new PQPushButton;
PQWidget *pbutton = (PQWidget*) button;

// есть еще и такой:
QWidget *widget= new PQWidget;
PQWidget *pwidget= (PQWidget*) widget;

Если я создам структуру не унаследованную от QObject, то я не смогу получить доступ к метасистеме для обычного QObject или QWidget - вот в чем проблема  Непонимающий
Записан
wxmaper
Гость
« Ответ #8 : Сентябрь 04, 2015, 11:50 »

Информация в отладчике очень противоречивая :/
как видно, и у QObject *timer, и у PQObject *ptimer поле myList существует...

Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #9 : Сентябрь 04, 2015, 12:06 »

Ну это понятно! Только кроме PQTimer мне нужен еще и сам PQObject со всеми плюшками Q_OBJECT'a...

Код:
// есть не только такой код:
QWidget *button= new PQPushButton;
PQWidget *pbutton = (PQWidget*) button;

// есть еще и такой:
QWidget *widget= new PQWidget;
PQWidget *pwidget= (PQWidget*) widget;

Если я создам структуру не унаследованную от QObject, то я не смогу получить доступ к метасистеме для обычного QObject или QWidget - вот в чем проблема  Непонимающий
Это популярное заблуждение. "Ах, нельзя делать множ наследование от QObject! Вот если б не было такого ограничения....". Предположим это так, и, (о чудо!) это разрешено. И мы бодро пишем напр
Код
C++ (Qt)
class CDream : public QWidget, public QTimer {
...
};
Но как это должно работать? Мы сможем связать сигнал или со слотом QWidget или QTimer. Все тоже для мета-системы - или один базовый класс или другой. И, получив сигнал, нам все равно нужно ломиться из одного базового в другой.  Какой же "выйгрыш"? Да никакого, зато новых забот полно - ведь "просто так" уже не законнектиться

В общем, "продумывайте архитектуру". Напр никто не мешает сделать так
Код
C++ (Qt)
class BasePObject : public QObject, public MyData {
Да, в методах MyData придется кросс-приводиться, но это не смертельно.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #10 : Сентябрь 04, 2015, 12:15 »

Информация в отладчике очень противоречивая :/
как видно, и у QObject *timer, и у PQObject *ptimer поле myList существует...
Создайте "чистый" (new PQObject) и его смотрите - у него нет полей таймера. А поскольку Вы привели "в стиле С" то смещение myList будет вычисляться как для PQObject
Записан
wxmaper
Гость
« Ответ #11 : Сентябрь 04, 2015, 12:49 »

И мы бодро пишем

Уж не знаю как вы так бодро пишете, но вот такое никогда не скомпилируется:
Код:
class PQObject : public QObject {
    Q_OBJECT

public:
    explicit PQObject(QObject *parent = 0)
        : QObject(parent){}

    virtual ~PQObject(){}

    QStringList myList;
};

class PQPushButton : public QPushButton, public PQObject {
public:
    explicit PQPushButton(QWidget *parent = 0)
        : QPushButton(parent)
    {
    }

    virtual ~PQPushButton(){}
};

QObject *button = new PQPushButton; // ошибка: 'QObject' is an ambiguous base of 'PQPushButton'

Получается мне придется создавать PQObject:
Код:
PQObject *button = new PQPushButton;
а его я уже никуда не смогу пропихнуть...

Записан
wxmaper
Гость
« Ответ #12 : Сентябрь 04, 2015, 12:55 »

а его я уже никуда не смогу пропихнуть...

ооо какую же я фигню сморозил Улыбающийся) PQObject пре-прекрасно кастуется в QObject! Веселый
Да я еще и Q_OBJECT везде совал, а он нужен только PQObject'y... да уж! Спасибо вам, Igors, вы меня спасли от жутких макросов и в корне неверных кастов Улыбающийся))
Самое печальное в этой ситуации то, что проблема вылезла когда проект вырос до размеров огромного неповоротливого слона... таких объектов у меня примерно штук 30-40 %( буду исправлять
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.453 секунд. Запросов: 23.