Модераторы: Snowy, MetalFan, bems, Poseidon

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Вызов деструктора внутри конструктора 
:(
    Опции темы
PsiMagistr
Дата 1.6.2010, 18:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 479
Регистрация: 31.12.2009

Репутация: 1
Всего: 1



Ребят, предположим у нас есть некий класс и в нем конструктор, куда передается строковый параметр FileName (Имя файла)

Возможно ли внутри реализации самого конструктора сделать проверку на существование файла и в зависимости от того, существует ли он - создавать экземпляр объекта или (если файла нет) выдать предупреждение и либо вовсе не создавать, либо разрушить объект?





Код


MyClass.Create(Filename:String);
Begin
Inherited Create;

If FileExist(FileName) then

................................... //Читаем файл.
....................................//Заполняем поля класса параметрами из файла.

else Иначе

Begin
ShowMessage('Файла нет!'); //Сообщение то появится...
self.Free; //А вот будет ли работать разрушение. Может объект еще создаться не успел? Может лучше просто покинуть метод  через Exit?
End;

End; //Внимание! Есть большие-большие сомнения, что этот код будет работать.




//Где то там в коде:

var MyObject : TMyClass; //Объявили переменную-указатель класса.

//Пытаемся создать объект:

MyObject := TMyClass.Create('Мой файл.txt'); Если файл существует, то создаем экземпляр, если нет -  даем предупреждение.



Кстати в каком именно месте идет команда на выдачу объекту динамической памяти? Заранее спасибо. 



Это сообщение отредактировал(а) PsiMagistr - 1.6.2010, 19:21


--------------------
"Арфы нет? Возьмите бубен!

Ребята, будем жить!"

 (с) "В бой идут одни старики"

---

"ИЕ" - один из самых сумасшедших браузеров в нашей галактике.
PM MAIL   Вверх
kami
Дата 1.6.2010, 19:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1806
Регистрация: 25.8.2007
Где: Санкт-Петербург

Репутация: 22
Всего: 72



Цитата(PsiMagistr @  1.6.2010,  18:07 Найти цитируемый пост)
Возможно ли внутри реализации самого конструктора сделать проверку на существование файла и в зависимости от того, существует ли он - создавать экземпляр объекта или (если файла нет) выдать предупреждение и либо вовсе не создавать, либо разрушить объект?

Нет. В самом конструкторе разрушать объект нельзя. Можно сделать "отложенное" удаление компонента/класса через PostMessage.
В данном случае лучшим выходом будет проверка в основном коде на существование файла и после этого уже вызов/не-вызов конструктора. Например, сделать классовый метод, типа
Код

class function CanCreate(params:...):boolean

Цитата(PsiMagistr @  1.6.2010,  18:07 Найти цитируемый пост)
Кстати в каком именно месте идет команда на выдачу объекту динамической памяти?

Afair, перед(во время) вызова конструктора. Ибо в самом конструкторе уже доступны все поля и методы созданного объекта.
PM MAIL WWW   Вверх
Демо
Дата 1.6.2010, 20:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1278
Регистрация: 3.11.2005

Репутация: 7
Всего: 50



Цитата(kami @  1.6.2010,  19:53 Найти цитируемый пост)
Нет. В самом конструкторе разрушать объект нельзя. 


Немного не так.

Цитата(PsiMagistr @  1.6.2010,  18:07 Найти цитируемый пост)
Возможно ли внутри реализации самого конструктора сделать проверку на существование файла и в зависимости от того, существует ли он - создавать экземпляр объекта или (если файла нет) выдать предупреждение и либо вовсе не создавать, либо разрушить объект?


Есть 2 метода. 
Один указал kami - использовать классовую функцию:

Код

TMyClass=class
public
  constructor Create(const FilePath: String);
  class function CreateMyClass(const FilePath: String): TMyCLass;
end;

class function CreateMyClass(const FilePath: String): TMyCLass;
begin
  Result := nil;
  if FileExists(FilePath) then Result := TMyClass.Create(FilePath);
end;


Второй метод - поднимать исключение в конструкторе.
На примере того же класса:

Код

TMyClass=class
public
  constructor Create(const FilePath: String);
//  class function CreateMyClass(const FilePath: String): TMyCLass;
end;

constructor TMyClass.Create(const FilePath: String);
begin
  if not FileExists(FilePath) then raise Exception.Create('File not exists');
//...
end;


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


--------------------
    
PM MAIL ICQ Skype   Вверх
kami
Дата 1.6.2010, 21:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1806
Регистрация: 25.8.2007
Где: Санкт-Петербург

Репутация: 22
Всего: 72



Цитата(Демо @  1.6.2010,  20:59 Найти цитируемый пост)
При возникновении в конструкторе исключения будет немедленно вызван деструктор, а объект не будет создан.

Ай-я... а ведь точно!
Спасибо, открыл глаза smile
PM MAIL WWW   Вверх
CodeMonkey
Дата 1.6.2010, 22:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1839
Регистрация: 24.6.2008
Где: Россия, Тверь

Репутация: 29
Всего: 89



TFileStream ведь так работает.


--------------------
Опытный программист на C++ легко решает любые не существующие в Паскале проблемы.
PM MAIL WWW ICQ Skype GTalk Jabber   Вверх
PsiMagistr
Дата 2.6.2010, 08:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 479
Регистрация: 31.12.2009

Репутация: 1
Всего: 1



СПА-СИ-БО ВСЕМ!



Цитата(Демо @  1.6.2010,  20:59 Найти цитируемый пост)
При возникновении в конструкторе исключения будет немедленно вызван деструктор, а объект не будет создан.
Во этом случае код в деструкторе нужно писать, учитывая, что в конструкторе может возникнуть исключение. 


1). А как именно можно словить это исключение в деструкторе? Ну хотя бы примерно? (Просто я о классах исключений слышал, но не более того. Очень поверхностно.)

2) Что есть "классовая функция"? (Даже не слыхал о таком.)



Ребята, когда-то я писал ролевую игру на VB. Несмотря на  то что я бросил проект, не могу сказать, что он не удался, т.к. все заложенные функции работали исправно, работа с файлами тоже была. Можно было даже бои устраивать и опыт набирать. Но там все было реализовано на уровне процедурного программирования. Ну не считая компонентов брошенных на формы. ))))

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

P.S. Потратил 2 часа написал на Дельфи "Пятнашки" (головоломка такая). Код вышел стремный, от балды, но работает.






Это сообщение отредактировал(а) PsiMagistr - 2.6.2010, 08:55


--------------------
"Арфы нет? Возьмите бубен!

Ребята, будем жить!"

 (с) "В бой идут одни старики"

---

"ИЕ" - один из самых сумасшедших браузеров в нашей галактике.
PM MAIL   Вверх
Демо
Дата 2.6.2010, 09:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1278
Регистрация: 3.11.2005

Репутация: 7
Всего: 50



Цитата(PsiMagistr @  2.6.2010,  08:53 Найти цитируемый пост)
1). А как именно можно словить это исключение в деструкторе? Ну хотя бы примерно? (Просто я о классах исключений слышал, но не более того. Очень поверхностно.)


А зачем в деструкторе его отдавливать?
Задача деструктора - освободить ресурсы и выполнить заключительные процедуры перед уничтожением.


Цитата(Демо @  1.6.2010,  20:59 Найти цитируемый пост)
Во этом случае код в деструкторе нужно писать, учитывая, что в конструкторе может возникнуть исключение.


Эту фразу надо понимать так: 

в деструкторе необходимо проверять, а действительно ли были в конструкторе выделены необходимые ресурсы.
В большинстве случаев это простейшие проверки: if Assigned() и т.п.

Цитата(PsiMagistr @  2.6.2010,  08:53 Найти цитируемый пост)
2) Что есть "классовая функция"? (Даже не слыхал о таком.)


class function/procedure - специальный вид функций, которые могут выполняться без создания экземпляра класса, не имеют доступа к ссылке на экземпляр класса (Self).

Пример приведён выше.


--------------------
    
PM MAIL ICQ Skype   Вверх
PsiMagistr
Дата 2.6.2010, 10:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 479
Регистрация: 31.12.2009

Репутация: 1
Всего: 1



Гм... Признаться не совсем понял. Ведь если ты сумел вызвать Free (деструктор) объекта, то значит сам объект уже был создан. Ведь он создается сразу при входе в конструктор.  Раз объект создан - память под него выделена. Зачем же нужны проверки - была ли выделена память?

Можно ли проверить в деструкторе, выделенна ли в конструкторе память так:

 If self <> nil then либо,

 if Assigned(self) = true then



Это сообщение отредактировал(а) PsiMagistr - 2.6.2010, 10:36


--------------------
"Арфы нет? Возьмите бубен!

Ребята, будем жить!"

 (с) "В бой идут одни старики"

---

"ИЕ" - один из самых сумасшедших браузеров в нашей галактике.
PM MAIL   Вверх
Демо
Дата 2.6.2010, 11:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1278
Регистрация: 3.11.2005

Репутация: 7
Всего: 50



Цитата(PsiMagistr @  2.6.2010,  10:29 Найти цитируемый пост)
Гм... Признаться не совсем понял. Ведь если ты сумел вызвать Free (деструктор) объекта, то значит сам объект уже был создан. Ведь он создается сразу при входе в конструктор.  Раз объект создан - память под него выделена. Зачем же нужны проверки - была ли выделена память?


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


--------------------
    
PM MAIL ICQ Skype   Вверх
CodeMonkey
Дата 2.6.2010, 13:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1839
Регистрация: 24.6.2008
Где: Россия, Тверь

Репутация: 29
Всего: 89



PsiMagistr, есть хороший пример и я вам в него ткнул:

Код
constructor TFileStream.Create(const AFileName: string; Mode: Word; Rights: Cardinal);
var
  LShareMode: Word;
begin
  if (Mode and fmCreate = fmCreate) then
  begin
    LShareMode := Mode and $FF;
    if LShareMode = $FF then
      LShareMode := fmShareExclusive; // For compat in case $FFFF passed as Mode
    inherited Create(FileCreate(AFileName, LShareMode, Rights));
    if FHandle = INVALID_HANDLE_VALUE then
      raise EFCreateError.CreateResFmt(@SFCreateErrorEx, [ExpandFileName(AFileName), SysErrorMessage(GetLastError)]);
  end
  else
  begin
    inherited Create(FileOpen(AFileName, Mode));
    if FHandle = INVALID_HANDLE_VALUE then
      raise EFOpenError.CreateResFmt(@SFOpenErrorEx, [ExpandFileName(AFileName), SysErrorMessage(GetLastError)]);
  end;
  FFileName := AFileName;
end;

destructor TFileStream.Destroy;
begin
  if FHandle <> INVALID_HANDLE_VALUE then
    FileClose(FHandle);
  inherited Destroy;
end;


Как видите, конструктор проводит создание объекта, инициализацию полей и т.п. Если при этом он сталкивается с ошибкой (например, не может открыть файл), он выбрасывает исключение. При этом Delphi автоматически вызывает деструктор. Иными словами, цепочка вызовов при этом выглядит так: 

CreateInstance (выделение памяти для Self) -> Create -> исключение -> Destroy -> FreeInstance (удаление памяти для Self). 

Сравните это со штатной ситуацией: 

CreateInstance (выделение памяти для Self) -> Create, далее вы работаете с объектом, вызывая в конце работы Free, Free -> Destroy -> FreeInstance (удаление памяти для Self).

Поскольку в случае возникновения ошибки конструктор выполняется не полностью, то деструктор должен быть готов к удалению частично-инициализированного объекта. Что означает, что не все поля в экземпляре будут заданы. В примере выше обратите внимание, как деструктор закрывает описатель файла только, если он был открыт (if FHandle <> INVALID_HANDLE_VALUE).


--------------------
Опытный программист на C++ легко решает любые не существующие в Паскале проблемы.
PM MAIL WWW ICQ Skype GTalk Jabber   Вверх
PsiMagistr
Дата 2.6.2010, 14:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 479
Регистрация: 31.12.2009

Репутация: 1
Всего: 1



CodeMonkey, благодарю.

Сижу, пробую, что то странное происходит. Но обо всем по порядку.

Создал проект.  Добавил новый модуль. В новый модуль подключил все заголовочные функции. Подключил сам модуль к модулю формы.

Описание класса (раздел interface добавленного модуля.)

Код

unit Unit2;

interface

type TMyClass = class //Начало описания класса
fName:String; //Одно общедоступное поле.
constructor Create(FileName:String); //Конструктор со строковым параметром (Имя файла) внутри.
Destructor Destroy; override; //Деструктор
end;


Реализация методов класса (Раздел implementation  модуля)

Код

constructor TMyClass.Create(FileName:String);
begin
inherited Create; //Вызов конструктора предка.
if FileExists(ExtractFilePath(Application.ExeName)+FileName) = true then //Если файл существует то:

begin
Fname := 'Значение по умолчанию';  //Задаем значение полю FName  и
ShowMessage('Файл есть!'); // Выводим сообщение
end

Else raise Exception.Create('Файла нет!'); Вызываем исключение, а значит деструктор.
end;


Destructor TMyClass.Destroy; Реализация деструктора.
Begin

If Assigned(self) then //Если указатель не равен nil связан с объектом то:
begin
ShowMessage('Сейчас вызову деструктор!'); //Даю сообщение и
Inherited Destroy; //Вызов деструктора предка, освобождение памяти.
end;

end;



На форму добавляю кнопку, пишу в разделе объявление глобальных переменных указатель класса:

Var  MyObject : TMyClass;

//В обработчике щелчка на кнопку:

MyObJect:=TMyClass.Create('Файл.txt'); //Рождение объекта. А файла нет. Должен сработать деструктор.


MyObject.fName := 'Поле'; {Эксперимент пытаюсь задать значение полю. 
 По идее если файла нет, то нет никакого объекта. 
(Точнее сработало исключение и тут же вызван  деструктор). 
А значит данная инструкция должна давать ошибку.  
Но она никакой ошибки не дает. 
При том что исключение в конструкторе срабатывает (Вижу сообщение самого исключения: 'Файла нет!' и сообщение "Сейчас вызову деструктор!")}




Такое впечатление, что  мой деструктор очищает не всю память...




Это сообщение отредактировал(а) PsiMagistr - 2.6.2010, 15:11


--------------------
"Арфы нет? Возьмите бубен!

Ребята, будем жить!"

 (с) "В бой идут одни старики"

---

"ИЕ" - один из самых сумасшедших браузеров в нашей галактике.
PM MAIL   Вверх
CodeMonkey
Дата 2.6.2010, 16:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1839
Регистрация: 24.6.2008
Где: Россия, Тверь

Репутация: 29
Всего: 89



Сыграем в игру (люблю я это дело) smile

Буратине дали три яблока. Сколько яблок у буратины? Думаете три? Ничего подобного. Никто же не сказал, сколько яблок было до того, как ему дали три.

Мораль истории: инициализируйте переменные.

Способны ли вы теперь с этой подсказкой найти объяснение видимому поведению? ;)


--------------------
Опытный программист на C++ легко решает любые не существующие в Паскале проблемы.
PM MAIL WWW ICQ Skype GTalk Jabber   Вверх
PsiMagistr
Дата 2.6.2010, 18:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 479
Регистрация: 31.12.2009

Репутация: 1
Всего: 1



Благодарю, CodeMonkey. ))) Развивайте мысль. )))) О каких переменных идет речь в данном случае? Насколько я понимаю, все переменные какими мы обладаем вроде получили инициализацию, во всяком случае так считает компилятор.

Внешний указатель вроде только один. MyObject.

переменная-поле внутри класса fName.

А теперь Ваши соображения? )









--------------------
"Арфы нет? Возьмите бубен!

Ребята, будем жить!"

 (с) "В бой идут одни старики"

---

"ИЕ" - один из самых сумасшедших браузеров в нашей галактике.
PM MAIL   Вверх
CodeMonkey
Дата 2.6.2010, 18:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1839
Регистрация: 24.6.2008
Где: Россия, Тверь

Репутация: 29
Всего: 89



Строка

Код
MyObJect:=TMyClass.Create('Файл.txt');


Раскладывается на более простые действия так: 

- вызвать конструктор
- ссылку, которую вернул конструктор, записать в переменную MyObject.

Соответственно, если конструктор возбуждает исключение, то выполнение прерывается и запись чего-либо в MyObject не происходит. Откуда следует, что в MyObject остаётся то, что было в ней до вышеуказанной строчки - т.е. мусор.

Очевидно, что вызов

Код
MyObject.fName := 'Поле';


Когда в MyObject лежит мусор, приводит к произвольному поведению. Это может быть нормальная работа, это может быть access violation - что угодно.

P.S. Проверка

Код
If Assigned(self) then


В деструкторе излишня - этим занимается Free. Уберите.


--------------------
Опытный программист на C++ легко решает любые не существующие в Паскале проблемы.
PM MAIL WWW ICQ Skype GTalk Jabber   Вверх
PsiMagistr
Дата 3.6.2010, 13:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 479
Регистрация: 31.12.2009

Репутация: 1
Всего: 1



Спасибо большое. 

 Точно. Вызывали мы объект. А он не вызвался (исключение). Что в осталось в указателе? А чушь полная... 



Неплохо было бы в деструкторе класса прописать чтобы указатель получал хотя бы Nil. Только конечная операция деструктора опять будет inherited Destroy и оно опять мусор зашлет в указатель...


Вы пишите If Assigned(self) then в деструкторе лишняя? Вот тут я не могу понять, почему? Да во Free есть эта встроенная проверка. Но именно Free нигде не вызывается.  До него дело не доходит, просто. Вызывается голый деструктор.

А попытка вызвать Free в деструкторе:

self.free; вместо inherited Destroy;

просто приведет к тому, что Free сам по себе станет вызывать деструктор, а деструктор - Free. И так без конца.






Это сообщение отредактировал(а) PsiMagistr - 3.6.2010, 13:12


--------------------
"Арфы нет? Возьмите бубен!

Ребята, будем жить!"

 (с) "В бой идут одни старики"

---

"ИЕ" - один из самых сумасшедших браузеров в нашей галактике.
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Delphi: Для новичков"
SnowyMetalFan
bemsPoseidon
Rrader

Запрещается!

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

  • Литературу по Дельфи обсуждаем здесь
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь
  • 90% ответов на свои вопросы можно найти в DRKB (Delphi Russian Knowledge Base) - крупнейшем в рунете сборнике материалов по Дельфи


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Snowy, MetalFan, bems, Poseidon, Rrader.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Delphi: Для новичков | Следующая тема »


 




[ Время генерации скрипта: 0.0712 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.