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

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

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Qt и управление памятью  (Прочитано 11722 раз)
0v.v0
Гость
« : Апрель 23, 2016, 10:05 »

Не нашёл похожей темы, поэтому начнём новую.
В общем, вопрос такой: как Qt Framework решает проблемы языка C++, связанные с управлением памятью, если не использует умные указатели?
1. Структура, которая владеет вложенными объектами, управляется через родителей QObject, тут всё понятно.
2. Если несколько объектов должны владеть объектом, уже начинаются вопросы. Например, 2 представления используют одну модель. Кто её будет освобождать? Пока что на примете единственный вариант - создавать эту модель в классе, который владеет всеми представлениями.
3. Но если двум объектам нужно получить слабый указатель друг на друга, идеи заканчиваются. Для вызова методов можно наклепать сигналов и слотов и соединять ближайшим общим родителем. А если нужно опросить чьи-то данные? Простой указатель не будет знать, можно ли обращаться по своему адресу, или память под ним была освобождена.
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #1 : Апрель 23, 2016, 10:09 »

А почему не использовать умные указатели?  Строит глазки
Записан
0v.v0
Гость
« Ответ #2 : Апрель 23, 2016, 10:30 »

Речь идёт о том, что объект добавляется в компоновщик, который автоматически будет его удалять. Если одновременно обернуть его shared_ptr, не возникнет проблем с двойным удалением? А может ещё и висяки образуются, если у объекта несколько владельцев и компоновщик сработает раньше деструктора указателей.
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #3 : Апрель 23, 2016, 10:49 »

Речь идёт о том, что объект добавляется в компоновщик, который автоматически будет его удалять. Если одновременно обернуть его shared_ptr, не возникнет проблем с двойным удалением? А может ещё и висяки образуются, если у объекта несколько владельцев и компоновщик сработает раньше деструктора указателей.
Для виджетов можно не использовать умные указатели (хотя ни каких проблем их использование не вызовет). Для виджетов мы вынуждены задавать парент не только для автоматического удаления детей, но и для правильного размещения виджетов.
А вот для невидимых данных использщовать умные указатели IMHO предпочтительней.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #4 : Апрель 23, 2016, 11:13 »

2. Если несколько объектов должны владеть объектом, уже начинаются вопросы. Например, 2 представления используют одну модель. Кто её будет освобождать? Пока что на примете единственный вариант - создавать эту модель в классе, который владеет всеми представлениями.
Здесь не видно возражений против shared_ptr (QSharedPointer), он напрашивается.

3. Но если двум объектам нужно получить слабый указатель друг на друга, идеи заканчиваются. Для вызова методов можно наклепать сигналов и слотов и соединять ближайшим общим родителем. А если нужно опросить чьи-то данные? Простой указатель не будет знать, можно ли обращаться по своему адресу, или память под ним была освобождена.
Я обычно делаю контейнеры для обоих. Пример: есть неск окон, в каждом картинки (возможно часть повторяется). Каждая картинка хранит указатели на окна в которых она используется, каждое окно - указатели используемых картинок. Сами картинки хранятся в своем контейнере. Теперь можно удалять как картинку так и окно, действия при удалениях очевидны.

..как Qt Framework решает проблемы языка C++, связанные с управлением памятью,
Я бы не назвал это "проблемой языка"  Улыбающийся
Записан
0v.v0
Гость
« Ответ #5 : Апрель 23, 2016, 12:11 »

Привожу конкретный пример. Предположим, что в объекте А создан объект "о", а в объекте В указатель "р". Сымитируем ситуацию удаления через контейнер объектов:

Код:
QObject *o = new QObject();
QObject *o1 = new QObject();
QObject *o  = new QObject(o1);
qDebug() << o;
QSharedPointer<QObject> p(o);
delete o1;
qDebug() << p.data();

Если запустить это пример, получим:
QObject(0x8ffdb8)
Программа неожиданно завершилась.
« Последнее редактирование: Апрель 23, 2016, 12:13 от 0v.v0 » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #6 : Апрель 23, 2016, 12:50 »

Если запустить это пример, получим:
QObject(0x8ffdb8)
Программа неожиданно завершилась.
Давайте в борщ свалим котлеты и кашу, а сверху еще зальем компотом, будете это кушать?  (впрочем недавно слышал что один французский аристократ так и делал).

Вы задействовали Qt parent-child которая предполагает что чайлды - простецкие указатели распределенные в куче (см deleteChildren). А потом добавили еще шареный указатель (который обязан удалять только сам) - ну конечно рухнет, т.к. 2 разные системы столкнулись лбами. Выберите что-то одно, по крайней мере для каждого экземпляра объекта.

Привожу конкретный пример.
В том-то и дело что никакой конкретики пока нет. Похоже Вы хотите найти какой-то общий "наилучший" метод чтобы применять его всегда и везде. Это не ново, но удачных попыток я не видел. "Сделаем все щаред" (типа избавиться от удаления вообще) обычно наталкивается на то что нередко Qt требует родителя. Возьмите живую конкретную задачу, скорее всего выяснится что изобретать ничего и не надо, т.е. проблема надумана.
Записан
0v.v0
Гость
« Ответ #7 : Апрель 24, 2016, 10:55 »

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

Дано: требуется создать два виджета, которые указывают друг на друга. В каждом из них кнопка и текстовое поле. По нажатии на кнопку окно сравнивает своё и чужое текстовое поле и если текст одинаковый - самоуничтожается,  иначе присваивает своей строке новый текст.

Стартовый код аварийно завершается.
Код:
#include <QtWidgets>

class MegaWidget : public QWidget
{
public:
    MegaWidget()
    {
        QVBoxLayout *layoutMain = new QVBoxLayout();
        {
            button = new QPushButton("close if equals");
            connect(button, &QPushButton::clicked, this, &MegaWidget::closeIfEquals);
            layoutMain->addWidget(button);

            line = new QLineEdit("text");
            layoutMain->addWidget(line);
        }
        setLayout(layoutMain);
        show();
    }

    void setOther(MegaWidget *other)
    {
        this->other = other;
    }

    QString getText()
    {
        return line->text();
    }

public slots:
    void closeIfEquals()
    {
        if (line->text() == other->getText())
            delete this;
        else
            line->setText(other->getText());
    }

private:
    MegaWidget *other;

    QPushButton *button;
    QLineEdit *line;
};

int main(int argc, char** argv)
{
    QApplication app(argc, argv);

    MegaWidget* w1 = new MegaWidget();
    MegaWidget* w2 = new MegaWidget();

    w1->setOther(w2);
    w2->setOther(w1);

    return app.exec();
}
Записан
Bepec
Гость
« Ответ #8 : Апрель 24, 2016, 11:08 »

ОМФГ... delete this это сильно написано Веселый
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #9 : Апрель 24, 2016, 12:35 »

Стартовый код аварийно завершается.
У меня вылетает при втором тычке (первый отрабатывает нормально). Да, если храним указатель, то нужно заботиться чтобы он был валиден. Для простых случаев хорош QPointer, напр
Код
C++ (Qt)
public slots:
   void closeIfEquals()
   {
if (other.isNull()) return;
       if (line->text() == other->getText())
           delete this;
       else
           line->setText(other->getText());
   }
 
private:
   QPointer<MegaWidget> other;
 
В более сложных случаях все равно придется заняться сигналами/оповещениями использующих указатель. Напр по-хорошему после первого закрытия нужно задизаблить кнопарь в оставшемся окне, напр
Код
C++ (Qt)
void closeIfEquals()
{
  if (other && (line->text() == other->getText())) {
    other->setOther(0);  
    delete this;
...
}
Более серьезная проблема - (де)сериализация указателей которыми объект не владеет.

ОМФГ... delete this это сильно написано Веселый
Никаким правилам языка это не противоречит, продолжаете использовать после этого - сами виноваты. Помню даже SDK с виртуальным методом DeleteThis (реализация delete this)
Записан
Bepec
Гость
« Ответ #10 : Апрель 24, 2016, 13:53 »

Угу, любое усложнение программы с сигналами и мы получаем большую бомбу замедленного действия.
Конечно сами виноваты. А лучше так вообще не делать Веселый
Записан
kambala
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4747



Просмотр профиля WWW
« Ответ #11 : Апрель 24, 2016, 15:34 »

может лучше deleteLater() вместо delete this?
Записан

Изучением C++ вымощена дорога в Qt.

UTF-8 has been around since 1993 and Unicode 2.0 since 1996; if you have created any 8-bit character content since 1996 in anything other than UTF-8, then I hate you. © Matt Gallagher
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #12 : Апрель 24, 2016, 16:56 »

может лучше deleteLater() вместо delete this?
Допустим если тексты равны надо удалить др окно (а не то где мы нажимали). Тогда
Код
C++ (Qt)
void closeIfEquals()
{
  if (other && (line->text() == other->getText())) {
    delete other;  
    other = 0;
...
Вряд ли этот текст вызовет какие-то вопросы, что же изменилось если this вместо other? Да ничего Улыбающийся А вдруг "чего-то случится", и мы вызовем метод разрушенного this? Так ведь с тем же успехом мы можем позвать метод разрушенного other - почему это никого не заботит? Улыбающийся

Наоборот, deleteLater потенциально опасен и должен применяться только когда нет др выхода.
Записан
lit-uriy
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3880


Просмотр профиля WWW
« Ответ #13 : Апрель 24, 2016, 19:30 »

"Наоборот, deleteLater потенциально опасен и должен применяться только когда нет др выхода." В Доке написано обратное.

т.к. он ждёт, когда закончатся все события для объекта.

П.С.
Некоторые люди не могут жить без проблем, поэтому всеми силами пытаются их себе создать.
Записан

Юра.
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #14 : Апрель 24, 2016, 21:08 »

Дано: требуется создать два виджета, которые указывают друг на друга.
На самом деле для решения этой задачи не нужны указатели друг на друга.
Каждый виджет при нажатии кнопки должен посылать сигнал с текстом в качестве аргумента. Тогда, вам нужно просто законектить сигнал одного виджета со слотом в другом. А при разрушении виджета связи разрываются автоматически.
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


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