Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Тупость компилятора  (Прочитано 15753 раз)
0 Пользователей и 4 Гостей смотрят эту тему.
zubr
Гость
« : 02-10-2008 03:53 » new

Код:
Ну а может моя тупость. В общем история такова. Создаю класс:
[code]
class __declspec(dllexport) CIEFrame
{
private:
   
public:
   CIEFrame();
   virtual ~CIEFrame();

public:
   // указатель на документ фрейма
   void* m_Document;
   // имя  фрейма
   CString Name;
   // индекс фрейма как элемента в списке элементов страницы
   int sourceIndex;
   // указатель на интерфейс IHTMLDocument2 документа фрейма
   void* m_pHtmlDocument;
};

CIEFrame::CIEFrame()
{
    m_Document = new CIEDocument();
};

CIEFrame::~CIEFrame()
{
     delete m_Document;
};
При тестировании программы обнаруживаю утечки памяти. Часа 2 убиваю (проект достаточно большой) на поиски. Обнаруживаю, что при вызове delete m_Document; не вызывается деструктор класса CIEDocument. После того как делаю:
Код:
CIEFrame::~CIEFrame()
{
     delete (CIEDocument*)m_Document;
};
утечки памяти пропадают.
Вот и не понятно:
1. При создании объекта m_Document = new CIEDocument(); компилятор знает, что создается объект CIEDocument, нафига ему для корректного удаления объекта нужно приведение типа.
2. И что же он удаляет в случае delete m_Document;[/code]
« Последнее редактирование: 02-10-2008 03:54 от zubr » Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #1 : 02-10-2008 04:26 » 

так всё правильно, а CIEDocument(); какой тип возвращает ? А ты указатель на void используешь. Вот компилятор и не знает, что ему бы ещё и деструктор вызвать Улыбаюсь
Он просто память по указателю освобождает, не заботясь о том, что там есть указатели на динамическую память (что делает деструктор)
Записан

zubr
Гость
« Ответ #2 : 02-10-2008 06:04 » 

Алексей1153++, указатель на void, но он то на объект CIEDocument указывает и компилятор об этом знает, бо создает этот объект, выделяет память под этот объект, инициализирует переменные в конструкторе и т. д., а при удалении только память освобождает не вызывая деструктор, вот это мне и не понятно.
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #3 : 02-10-2008 06:07 » 

zubr, компилятор делает то, что он знает о типе указателя, когда удаляет память. Для войд - он ничего не вызовет, лишь память освободит. Для твоего типа - он знает, что надо вызвать из виртуальной таблицы нужный деструктор (который подчистит неведомые компилятору хвосты), а только потом поступит с памятью точно так же, как с войдом
Записан

McZim
Команда клуба

ru
Offline Offline
Пол: Мужской
Я странный


WWW
« Ответ #4 : 02-10-2008 06:08 » 

zubr, сделай так

Код:
public:
   CIEDocument* m_Document;

...
...
...

CIEFrame::CIEFrame()
{
    m_Document = new CIEDocument();
};

CIEFrame::~CIEFrame()
{
     delete m_Document;
};
Записан

The CBO without stats is like a morning without coffee. (c) T.Kyte.
zubr
Гость
« Ответ #5 : 02-10-2008 06:20 » 

McZim, в общем то это не проблема, а философский вопрос (о тупости компилятора). А сделать CIEDocument* m_Document; не могу, так как пересечение возникает. Членом класса CIEDocument является объект класса CIEFrame и наоборот.
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #6 : 02-10-2008 06:25 » 

Зубр , пересечение легко решается

разнести коды классов в пары файлов .h и .cpp . В заголовочных нужно перед определением класса написать предопределение

class mytype1;
class mytype2;
...

далее в заголовке компилятор позволит определять указатели на эти классы

ну и не забыть в cpp вставить инклуды заголовков нужных классов
Записан

McZim
Команда клуба

ru
Offline Offline
Пол: Мужской
Я странный


WWW
« Ответ #7 : 02-10-2008 06:28 » 

zubr, ну Леха собственно все сказал, так что это не тупость компилятора.
Записан

The CBO without stats is like a morning without coffee. (c) T.Kyte.
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #8 : 02-10-2008 06:29 » 

это вообще ничья тупость, все по своему правы - и Зубр , и компилятор ) Но придётся всё таки идти на поводу компилятора, потому что так правильно
Записан

npak
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #9 : 03-10-2008 09:02 » 

То, что что хочет zubr называется статическим анализом - компилятор должен проглядеть весь код программы, убедиться, что в m_Document может оказаться только указатель на CIEDocument.

К сожалению, для С++ это пока не решенная теоретическая проблема. Она решена для спецефических языков, вроде Ocaml, но эти языки по эффективности исполнения с С++ и рядом не стоят.

Если же поглядеть в стандарт (раздел 12.4 Destructors), то можно обнаружить такой текст:
Цитата
a destructor for class X calls the destructors for X’s direct members

Так вот, в языке С++ деструкторы есть только у инстансов классов, у указателей и ссылок деструкторов нет! Пусть даже они указывают самый разобъектный объект с самым деструкторым деструктором, но для компилятора это не имеет значение - если программист пользуется указателями, пускай сам прибирает за собой.

Объяснение очевидно. В С++ нет подсчета ссылок, поэтому компилятор не может дать никаких гарантий, что в момент удаления фрейма на объект, на который указывает m_Document, больше никто не ссылается. Если такие ссылки есть, то после удаления m_Document они провиснут, и обращения к ним приведут, скорее всего, к краху. Разработчики языка решили, что пусть лучше утекает память, чем падают приложения.

Если так хочется писать не думая, то следует пользоваться умными указателями. Они являются объектами (а не ссылками), поэтому для них вызывается автоматический деструктор, встроенный счетчик ссылок контролирует, когда будет удалена последняя ссылка на объект, и удаляет только те, указатели, которыми уже никто не пользуется.
Записан

UniTesK -- индустриальная технология надежного тестирования.

http://www.unitesk.com/ru/
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #10 : 03-10-2008 09:08 » 

npak, что значит у указателей и ссылок нет деструкторов ? То есть, прямо у них нет, конечно, но деструктор вызовется - тип указателя известен
Записан

Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #11 : 03-10-2008 10:17 » 

Объяснение очевидно. В С++ нет подсчета ссылок, поэтому компилятор не может дать никаких гарантий, что в момент удаления фрейма на объект, на который указывает m_Document, больше никто не ссылается. Если такие ссылки есть, то после удаления m_Document они провиснут, и обращения к ним приведут, скорее всего, к краху. Разработчики языка решили, что пусть лучше утекает память, чем падают приложения.
Не понял. Память будет освобождена и с некоторой вероятностью использована снова - и в этом случае всё равно всё упадёт. То, что не будет вызван деструктор, лишь замаскирует проблему. Разве нет?
Записан
npak
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #12 : 03-10-2008 12:07 » 

npak, что значит у указателей и ссылок нет деструкторов ? То есть, прямо у них нет, конечно, но деструктор вызовется - тип указателя известен

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

Код:
class C {
    SomeClass m_instance;
    SomeClass * m_ptr;
    SomeClass & m_ref;
};

Для класса С компилятор сгенерирует имплицитный деструктор, который вызовет деструктор объекта m_instance. Два остальных члена класса объектами не являются, деструкторов у них нет, поэтому компилятор для их обработки ничего генерировать не будет.
Записан

UniTesK -- индустриальная технология надежного тестирования.

http://www.unitesk.com/ru/
npak
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #13 : 03-10-2008 12:30 » 

Не понял. Память будет освобождена и с некоторой вероятностью использована снова - и в этом случае всё равно всё упадёт. То, что не будет вызван деструктор, лишь замаскирует проблему. Разве нет?

Если деструктор для *(m_Document) вызван не будет, то память по адресу m_Document не освободиться. Поэтому если кто-то еще содержит указатели на этот адрес, он сможет спокойно читать или писать по этому адресу без риска навредить кому-то еще.  Если же неявный деструктор сделает delete m_Document, то результат предсказать я не берусь. Могут быть самые разные эффекты.
Записан

UniTesK -- индустриальная технология надежного тестирования.

http://www.unitesk.com/ru/
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #14 : 03-10-2008 16:16 » 

насчёт того, что будет лежать по указателю, об этом программист позаботится.

а вот это
Цитата
. Два остальных члена класса объектами не являются, деструкторов у них нет, поэтому компилятор для их обработки ничего генерировать не будет.
со ссылкой всё понятно, с указателем - без вызова delete конечно ничего не произойдёт. Речь то именно про delete была
Записан

zubr
Гость
« Ответ #15 : 03-10-2008 18:41 » 

Цитата
Если деструктор для *(m_Document) вызван не будет, то память по адресу m_Document не освободиться.
Почему, освободится, но частично. То есть не освободится память объектов, создаваемых в m_Document динамически и уничтожаемых в деструкторе. Как я понимаю статические объекты все таки должны удалиться и без деструктора.
Записан
Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #16 : 03-10-2008 18:54 » 

zubr, вот и я так думаю... Ведь будет освобождён весь блок памяти, выделенный под документ - то есть, размером со всё статическое содержимое (включая указатели - что и приведёт к утечке объектов, на которые они указывают).
Поэтому мне не совсем понятно высказывание npak насчёт краха и утечек. Удалишь при наличии других ссылок - сломается, не удалишь при отсутствии этих ссылок - будут утечки.
Записан
npak
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #17 : 04-10-2008 16:49 » 

Удалишь при наличии других ссылок - сломается, не удалишь при отсутствии этих ссылок - будут утечки.

Именно так.

Язык С++ предлагает программисту самому следить за чистотой памяти. Деструкторы вызываются только для тех объектов, которые размещаются внутри удаляемого объекта. Кстати, в свое время Борланд С++ выдавал предупреждение, если метод возвращал указатель на член класса. Объясняли это так - время жизни поля объекта ограничено временем жизни самого объекта, а время жизни внешнего указателя может быть больше, то есть возможен случай, когда объект уже удален со всеми полями, а указатель на поле еще существует. Создатели компилятора в одной из версий такие предупреждения давали, а потом, насколько помню, перестали.

Поскольку проблема общих указателей очень распространена, существуют довольно эффективные решения. Наиболее популярны умные указатели (их я уже упоминал). Правда, они не умеют справляться с циклическими ссылками, но и для этой ситуации есть типовые решения - слабые (weak) указатели.

Так что, если хотите, чтобы компилятор сам подбирал память для указателей, Welcome to Boost!

http://www.codeproject.com/KB/stl/boostsmartptr.aspx
http://www.boost.org/doc/libs/1_36_0/libs/smart_ptr/smart_ptr.htm
Записан

UniTesK -- индустриальная технология надежного тестирования.

http://www.unitesk.com/ru/
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines