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

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

Страниц: 1 ... 4 5 [6] 7 8 ... 10   Вниз
  Печать  
Автор Тема: Основы удаления объектов  (Прочитано 85998 раз)
ilot
Гость
« Ответ #75 : Январь 09, 2010, 10:22 »

Так в том то и вопрос - зачем он в вашем примере виртуальный? Что это даёт в вашем примере? Почему деструктор QObject не обычный в вашем примере?
Потому что тролли сделали его виртуальным. Веселый Если вы еще не поняли, то в примере я использовал QObject из библиотеки Qt. Подмигивающий

Примеры не демонстрируют аналогии, а показывают вариант развития событий. Ваш пример не показывает необходимость полиморфизма. Потрудитесь написать пример, в котором полиморфизм таки будет продемонстрирован. Вот тогда и посчитаем количество new/delete в коде.
Да я вам уже кусок кода из реализации QObject привел, в котором выполняется зачистка:
Код:
void QObjectPrivate::deleteChildren()
{
    ......
    for (int i = 0; i < children.count(); ++i) {
        currentChildBeingDeleted = children.at(i);
        children[i] = 0;
        delete currentChildBeingDeleted;//вот здесь
    }
    ......
}
Для каждого потомка вызов delete выполняется явно (children.count() раз). currentChildBeingDeleted - имеет тип QObject*. И полиморфизм используется для того, чтобы вызывался не ~QObject(), а деструкторы тех объектов, которые в действительности являются потомками (~QLabel(), ~QLineEdit, ~QPushButton() и т.п.). Пример с Foo я привел для наглядности, как демонстрация применения виртуального деструктора, и что он никак не сокращает количество вызовов delete.

Даже не знаю, что еще добавить. Понимаю, тема сложная - остается только посоветовать доп. литературу... Например, в Thinking in C++, Брюса Эккеля, полиморфизм и применения виртуальных деструкторов неплохо раскрыты.
Записан
Dendy
Гость
« Ответ #76 : Январь 09, 2010, 10:47 »

Этот пример как раз и показывает ровно один delete в коде библиотеки на все new производных от QObject'а классов.

Я вижу вы мастер подменять понятия. В третий раз повторю о чём речь:

Речь идёт о количестве записей new/delete в коде, а не о количестве их вызовов во время выполнения.

С чем вы выше по тексту согласились:

> вызовов

Как будто специально не читаете.
переформулирую вопрос:
И как же наличие виртуального деструктора позволит сократить количество записей delete в программе??

После чего вы умело вставляете слова "в коде":

Сами по себе виртуальные деструкторы никак не помогают избежать явного вызова delete в коде.

И последнее ваше перевоплощение, слова "в коде" магическим образом из вашего текста исчезают:

Пример с Foo я привел для наглядности, как демонстрация применения виртуального деструктора, и что он никак не сокращает количество вызовов delete.

Или вы делаете это специально, чтобы запутать других, или действительно не улавливаете суть разговора.
Записан
ilot
Гость
« Ответ #77 : Январь 09, 2010, 11:14 »

Этот пример как раз и показывает ровно один delete в коде библиотеки на все new производных от QObject'а классов.
Очень смешно... Это вообще-то опять заслуга структурной организации: все указатели хранятся в контейнере QList, что делает возможным их перебор в цикле for. Причем здесь концепция виртуальных вызовов - уму непостижимо. Так можно обрабатывать коллекции любых элементов без всякого полиморфизма.

Я вижу вы мастер подменять понятия. В третий раз повторю о чём речь:
Речь идёт о количестве записей new/delete в коде, а не о количестве их вызовов во время выполнения.

С чем вы выше по тексту согласились:
...
После чего вы умело вставляете слова "в коде":

Сами по себе виртуальные деструкторы никак не помогают избежать явного вызова delete в коде.

И последнее ваше перевоплощение, слова "в коде" магическим образом из вашего текста исчезают:

Пример с Foo я привел для наглядности, как демонстрация применения виртуального деструктора, и что он никак не сокращает количество вызовов delete.
Я с этим согласился, и нигде это больше перевирал. Под словами "в коде" я понимал исходный код программы (вызовы в исходном коде, а не во время выполнения). Не знаю, почему вы решили, что я снова имею ввиду вызовы во время выполнения.
Записан
Dendy
Гость
« Ответ #78 : Январь 09, 2010, 11:42 »

Вы уж определитесь, нужен для QObject виртуальный деструктор или не нужен. А то вначале вы пишите:

И полиморфизм используется для того, чтобы вызывался не ~QObject(), а деструкторы тех объектов, которые в действительности являются потомками (~QLabel(), ~QLineEdit, ~QPushButton() и т.п.).

А после этого:

Причем здесь концепция виртуальных вызовов - уму непостижимо.

А на счёт этого:

Пример с Foo я привел для наглядности, как демонстрация применения виртуального деструктора, и что он никак не сокращает количество вызовов delete.
Не знаю, почему вы решили, что я снова имею ввиду вызовы во время выполнения.

Во-первых, потому что именно это вы и написали: "никак не сокращает количество вызовов delete". Во-вторых, я вам уже рассказал, что ваш пример демонстрирует ровным счётом ничего. Зачем вам там виртуальный деструктор - вы до сих пор не обьяснили.

Я конечно спрошу ещё раз, хотя думаю вы опять проигнорируете:

Вопрос номер 1: Зачем вам там виртуальный деструктор, если вызов конструктора Foo и его удаление в одной области видимости?
Вопрос номер 2: Почему вы обьявили указатель pobj как QObject*, а не как Foo*?
Записан
BRE
Гость
« Ответ #79 : Январь 09, 2010, 12:04 »

Бзззз! Ответ неверен.

kibsoft, внимание, отвечает Александр Друзь.

1. На вопрос удаляются ли дочерние виджеты, ответ - да, удаляются.
2. На вопрос почему не вызывается слот crash(), ответ - не потому что соединение было разорвано.
3. На вопрос "а почему тогда?" ответ кроется в недрах C++.

Qt использует виртуальные методы для вызова методов по имени (сигналов и слотов). А как мы знаем из теории - виртуальные методы в деструкторах не работают, поскольку отнаследованный обьект (ваш Widget) был разрушен до базового (QObject). Именно в деструкторе QObject удаляюся дочерние обьекты, сигналы destroyed() от которых уже не могут вызвать виртуальный метод в Widget.
Давайте смотреть....
Разрушение детей QWidget происходит не в деструкторе QObject, а в деструкторе QWidget (Qt 4.6)!
Смотрим код:
Код
C++ (Qt)
QWidget::~QWidget()
{
   ...
   if (!d->children.isEmpty())
       d->deleteChildren();
   ...
}
 
Также, если посмотреть код деструктора QObject, то легко можно видеть, что сначала происходит разрывание всех связей (коннектов) уничтожаемого объекта, а потом уже разрушаются его дети.
Записан
Dendy
Гость
« Ответ #80 : Январь 09, 2010, 12:13 »

BRE, да вы правы, очевидно удаление виджетов связано с особенностями оконной системы, поэтому в ~QWidget() они удаляются явно. В любом случае удаление дочерних обьектов происходит уже после того как отработал деструктор собственного класса и разрушилась его таблица виртуальных методов. Даже если бы дочерние обьекты удалялись после разрыва соединений - всё равно сигнал бы не пришёл.
Записан
ilot
Гость
« Ответ #81 : Январь 09, 2010, 12:25 »

Вы уж определитесь, нужен для QObject виртуальный деструктор или не нужен. А то вначале вы пишите:
Фразы, выдранные из контекста..
В первом случае я говорил о том, как используется виртуальность деструктора ~QObject(), и там же (уже не в первый раз) указал, что на количество записей delete в исходном коде программы виртуальность деструктора никак не влияет. И что нужна эта виртуальность только для организации правильной зачистки памяти (пример с Foo это наглядно демонстрирует; собственно для этого я его и делал).
Во втором случае я комментировал вашу фразу:
Этот пример как раз и показывает ровно один delete в коде библиотеки на все new производных от QObject'а классов.
Я аргументировал, что "один delete"  - результат структурной организации (использование списка и перебор данного списка в цикле), а не результат работы виртуального деструктора.
Вы уж определитесь, нужен для QObject виртуальный деструктор или не нужен.
Нужен для правильной зачистки памяти, при этом никакого сокращения количества записей delete он не дает. В моих постах никакой неопределенности по этому вопросу не просматривается.

А вообще диалог зашел в тупик. Я вам уже и оригинальный код из библиотеки Qt приводил, и собственный пример написал, и прокомментировал все - что и зачем. Вы же вместо аргументированных ответов все пытаетесь подловить меня на мнимых противоречиях. Пора бы разбавить темку еще чьим нибудь мнением...
Записан
ilot
Гость
« Ответ #82 : Январь 09, 2010, 12:33 »

В любом случае удаление дочерних обьектов происходит уже после того как отработал деструктор собственного класса и разрушилась его таблица виртуальных методов.
Удаление дочерних объект происходит во время работы деструктора родителя. Там после
Код:
if (!d->children.isEmpty())
        d->deleteChildren();
еще код есть  Подмигивающий

Это значит, что когда уже все потомки удалены, родитель некоторое время продолжает существовать.
« Последнее редактирование: Январь 09, 2010, 12:36 от ilot » Записан
Dendy
Гость
« Ответ #83 : Январь 09, 2010, 12:39 »

пример с Foo это наглядно демонстрирует

Пример с Foo не демонстрирует абсолютно ничего. Вы уже в который раз на него ссылаетесь и в который раз я вам это говорю. Почему - обьяснил выше.

Вы же вместо аргументированных ответов...

Ответов на какие вопросы? Не видел ни одного.

А вот мои вопросы, которые я задавал несколько раз так и остались без ответа. Просьба написать пример, где демонстрируется полиморфизм тоже была проигнорирована.

Спрошу в третий раз, интересно, проигнорируете ли вы снова:
Вопрос: Почему вы обьявили указатель pobj как QObject*, а не как Foo*, если вызов конструктора Foo и его удаление в одной области видимости?
Записан
Dendy
Гость
« Ответ #84 : Январь 09, 2010, 12:54 »

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

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

Сообщений: 11445


Просмотр профиля
« Ответ #85 : Январь 09, 2010, 13:57 »

http://rghost.ru/806868 вот проект..
Все children'ы удаляются автоматычно, все корректно. Да, destroyed в данном случае не приходит потому что, как указал BRE, коннект уже разорван. А разорвать его было необходимо потому что widget уже разрушен (как указал Dendy). Поставьте слот на что-нибудь живущее - и destroyed будет приходить. А убедиться удаляются ли children'ы проще не связываясь с destroyed а напр. так

Код:
class MyLabel : public QLabel {
 public:
  MyLabel( const QString & txt, QWidget * parent = 0 ) : QLabel(txt, parent) {}
 ~MyLabel( void ) { qDebug() << "MyLabel destroyed"; }
};

Edit: перечитав, вижу что Авварон уже ответил - но разве в такой свалке что-то разберешь?  Улыбающийся
« Последнее редактирование: Январь 09, 2010, 15:02 от Igors » Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #86 : Январь 09, 2010, 15:36 »

ненене, там там всё хитрее чем я написал... а тему надо закрыть и отправить спорщиков читать всё с самого начала. Было предложено ТРИ варианта почему не приходят сигналы. А холиварить о словах "явное указание delete в коде" просто бессмысленно
Записан
Dendy
Гость
« Ответ #87 : Январь 09, 2010, 16:05 »

Авварон, а вы, я так понимаю, тот самый Д'Артаньян.
Записан
ilot
Гость
« Ответ #88 : Январь 09, 2010, 17:01 »

Ответов на какие вопросы? Не видел ни одного.
Ответы на мои посты. Их (ваших ответов) предостаточно.
Спрошу в третий раз, интересно, проигнорируете ли вы снова:
Вопрос: Почему вы обьявили указатель pobj как QObject*, а не как Foo*, если вызов конструктора Foo и его удаление в одной области видимости?
Я ни разу не игнорировал ваши вопросы. Отвечал как мог. Отвечу еще раз: только для того, чтобы продемонстрировать работу виртуального деструктора - чтобы удалять экземпляр Foo через указатель типа Foo* виртуальный деструктор не нужен. Чтобы продемонстрировать работу виртуального деструктора именно в том виде, в каком это используется при зачистке списка потомков в деструкторе ~QObject().

Присмотритесь что вы комментируете. Деструкторов у обьекта несколько, вызываются они от порождённого класса к базовому. И удаление дочерних обьектов происходит как раз после собственного деструктора. А значит в таблице виртуальных методов обьекта уже будут отсутствовать указатели на переопределённые виртуальные методы (qt_metacall() и другие) собственного класса, потому они и не вызовутся, потому и не вызовутся слоты.
Dendy, вы все правильно говорите, но вы меня не поняли... Грустный Разбор объекта действительно происходит начиная с деструктора порожденного класса и, двигаясь вверх по иерархии наследования, заканчивается вызовом деструктора базового класса. Все как вы говорите.. но дело в том, что отношение "родитель-потомок" в контексте библиотеки Qt это не тоже самое, что отношение "базовый класс - производный класс" в контексте наследования. В Qt объект ("родитель") хранит список указателей на зависящие от него объекты (для них применяется термин "дочерние"). С точки зрения стандарта С++ такое отношение называется композицией. Перед тем как объект перестанет существовать, он обязан удалить все данные за которые он отвечает. Если он не удалить все эти объекты, указатели на которые он хранит ("дочерние"), до завершения своего деструктора, то их уже не удалит никто, поскольку этих указателей больше не будет (а сами "дочерние" объекты останутся в памяти).

А холиварить о словах "явное указание delete в коде" просто бессмысленно
Согласен. Я, честно говоря, думал, что Dendy просто случайно сослался на концепцию полиморфизма, как средство, позволяющее не заботится о вызовах delete. Оказалось нет. А тема эта довольно важная (как в чистом C++, так и в контексте библиотеки Qt), поэтому странно, что никто больше не высказался (может быть тогда и пришли к какому-то заключению).

P.S. трудно наполнить чашу, которая уже полна.
« Последнее редактирование: Январь 09, 2010, 17:11 от ilot » Записан
Dendy
Гость
« Ответ #89 : Январь 09, 2010, 17:38 »

Попробую отрезюмировать. На основании стандарта C++ и собственного опыта разработки я могу заключить следующее: количество new/delete в коде никак друг с другом не соотносятся. Вот пример с несколькими delete и одним new:

Код
C++ (Qt)
typedef int A;
 
class B
{
public:
   A * create( int index )
   {
       return values_[ index ] = new A;
   }
 
   void destroy( int index )
   {
       delete values_.take( index );
   }
 
   void clear()
   {
       foreach ( A * value, values_ )
           delete value;
       values_.clear();
   }
 
private:
   QHash<int,A*> values_;
};
 

Посчитайте new и delete. Это живой пример, их количество никак не соотносится. Вариантов конструирования и уничтожения может быть множество.

За счёт виртуальных деструкторов и шаблонов уничтожение обьектов можно полностью локализировать в библиотечные функции, снизив их количество в клиентском коде до нуля. Попробуйте написать пример класса с виртуальным деструктором, количество new/delete в коде для которого будет один к одному. Такое просто невозможно. Хотя для интереса таки стоит, очень способствует саморазвитию.

Возьмите другие языки - Java/C#, там вообще отсутствует явное управление памятью, на высоком уровне вам дают оперировать разве что счётчиками ссылок. Соотношение явных аллокаций к уничтожениям - N/0. Возьмите Objective-C, там весь рантайм построен на основе retain/release, тем не менее абсолютное большинство обьектов после аллокации помещаются в autorelease pool, а некоторым сразу же вызывается autorelease, чтобы случайно не забыть про деаллокацию где-то там в конце функции. Явное удаление способствует ошибкам при работе с памятью - где-то забыли удалить, получили утечку, где-то удалили два раза, получили креш. Каждый язык/фреймворк предоставляет свои механизмы для деаллокации данных. Писать на каждый new свой delete в C++ - глупость.
Записан
Страниц: 1 ... 4 5 [6] 7 8 ... 10   Вверх
  Печать  
 
Перейти в:  


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