Как отменить создание класса, если в процессе работы конструктора обнаружилось, что какой-нибудь необходимый ресурс занят, или по другой проичине.
Можно ли прямо в конструкторе вызвать метод Free?
Достаточно ли вызвать какой-нибудь exception?
Здравствуйте, defrag, Вы писали:
D>Как отменить создание класса, если в процессе работы конструктора обнаружилось, что какой-нибудь необходимый ресурс занят, или по другой проичине.
Если в конструкторе возникло исключение, то создание объекта завершается, и все выделенные ресурсы возвращаются "системе" автоматически менеджером "системных ресурсов" дельфы (в смысле, менеджером памяти). Все это можно прочитать в хелпе, прямо в первой же статье про конструкторы.
Именно поэтому рекомендована следующая запись:
var
vObj: TSomeObject;
begin
vObj:=TSomeObject.Create(...);
try{ some usefull code }
...
finally
vObj.Free;
end;
end;
D>Можно ли прямо в конструкторе вызвать метод Free?
Нет, я бы не рекомендовал это делать. Особенно если деструктор будет вызывать какие-то Free или Dispose или даже FreeMem, а объектов конструктор еще не создал. D>Достаточно ли вызвать какой-нибудь exception?
Более чем достаточно, кроме того исключение может выкинуть вызванная тобой в конструкторе функция.
Никогда не бойся браться делать то, что делать не умеешь. Помни, ковчег был построен любителем. Профессионалы построили Титаник...
D>>Можно ли прямо в конструкторе вызвать метод Free? VVP>Нет, я бы не рекомендовал это делать. Особенно если деструктор будет вызывать какие-то Free или Dispose или даже FreeMem, а объектов конструктор еще не создал.
А еще в хелпе написано:
If an exception is raised during execution of a constructor that was invoked on a class reference, the Destroy destructor is automatically called to destroy the unfinished object.
Если в конструкторе будет обнаружено, что создать объект невозможно,
в нём следует возбудить исключение.
При этом он будет автоматически уничтожен, как уже указал PepperLover.
(правда, ф-я BeforeDestructor при этом не будет вызвана).
Корректное уничтотожение в деструкторе ещё не созданных объетов-членов
гарантируется тем, что при создании объекта все они были обнулены.
(Поэтому уничтожать объекты-члены следует всегда через Free, которая проверяет
объект на nil).
VVP>Именно поэтому рекомендована следующая запись:
Это совсем из другой оперы. Такая запись гарантирует, что если в
some usefull code (а отнюдь не в конструкторе) произойдёт исключение,
то объект будет уничтожен. Если исключение будет возбуждено в
конструкторе, то никакой код после него вызван не будет, а освободится
объект автоматически.
R>Если в конструкторе будет обнаружено, что создать объект невозможно, R>в нём следует возбудить исключение.
И к этому следует добавить, что (может быть),
если надо просто вернуть после конструктора nil,
без вываливания из программы и заключения его в try...end;
процедуру Abort; (Raises a silent exception. unit SysUtils)
Здравствуйте, PepperLover, Вы писали:
PL>И к этому следует добавить, что (может быть), PL>если надо просто вернуть после конструктора nil, PL>без вываливания из программы и заключения его в try...end; PL>процедуру Abort; (Raises a silent exception. unit SysUtils)
При этом конструктор вовсе не вернёт нил.
Будет всё тоже самое "вываливание из программы", что и при обычном исключении,
только не выскочит окошко с сообщением об ошибке.
Здравствуйте, rounin, Вы писали:
R>Здравствуйте, VVP, Вы писали:
R>Если в конструкторе будет обнаружено, что создать объект невозможно, R>в нём следует возбудить исключение.
С этим я полностью согласен. В случае возникновения исключительной ситуации в конструкторе, автоматически вызывается его деструктор, так что отпадает проблема заботиться в том, что проинициализировано, а что нет. Например:
constructor TTest.Create();
begin
inherited Create();
FObj1 := TObj1.Create;
if not CheckResource() then raise ETest.Create(SMessage);
FObj2 := TObj2.Create;
end;
destructor TTest.Destroy();
begin
FObj2.Free;
FObj1.Free;
inherited;
end;
procedure SomeProc;
begin
FTest := TTest.Create;
try// .....finally
FTest.Free;
end;
end;
На более низком уровне, текст SomeProc выглядит примерно так:
> В случае возникновения исключительной ситуации в конструкторе,
автоматически вызывается его деструктор, > так что отпадает проблема заботиться в том, что проинициализировано, а что
нет.
destructor TTest.Destroy();
begin
if Assigned(FObj2) then
FObj2.Free;
if Assigned(FObj1) then
FObj1.Free;
inherited;
end;
еще и потому что
"Unlike Destroy, Free is successful even if the object is nil;
so if the object was never initialized, Free won’t result in an error."
применительно к иным ресурсам, например Handle, очень желательно было бы
в деструкторе проверять выделен ли он (создан, открыт)
--
"Software is like sex it's better when it's free." (с) Linus Torvalds
Posted via RSDN NNTP Server 1.7 "Bedlam"
Только на РСДН помимо ответа на вопрос, можно получить еще список орфографических ошибок и узнать что-то новое из грамматики английского языка (c) http://www.rsdn.ru/forum/cpp/4720035.1.aspx
PL>>И к этому следует добавить, что (может быть), PL>>если надо просто вернуть после конструктора nil, PL>>без вываливания из программы и заключения его в try...end; PL>>процедуру Abort; (Raises a silent exception. unit SysUtils)
R>При этом конструктор вовсе не вернёт нил. R>Будет всё тоже самое "вываливание из программы", что и при обычном исключении, R>только не выскочит окошко с сообщением об ошибке.
Специально проверил на Delphi 7
Сделал такую конструкцию:
type
tPiper = class(tObject)
constructor Create;
end;
constructor tPiper.Create;
begin
inherited Create;
Abort;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
P : tPiper;
begin//P := nil;if P = nil
then Memo1.Lines.Add('1. nil')
else Memo1.Lines.Add('1. NO nil');
P := tPiper.Create;
if P = nil
then Memo1.Lines.Add('2. nil')
else Memo1.Lines.Add('2. NO nil');
Memo1.Lines.Add('Last');
end;
Так вот она вываливается только из Button1Click, но не из программы!
И никакого окошка не выходит (silent exception !!!)!
Last не печатается!
Но если поставить P := tPiper.Create;
в try...except и
Если объявить P:=nil; (убрать //) то печатается
2. nil
Если не убирать //P:=nil;
то будет 2. NO nil
В случае любого исключения происходит "вываливание" до первого (самого внутреннего
из вложенных) обработчика данного исключения.
Код вне блока try-except, в котором определён обработчик
данного исключения, будет выполнен, как будто никакого исключения не было.
Если же обработчика исключения не найдётся, происходит аварийное завершение программы.
Никаких исключений для EAbort здесь нет.
В случае стандартных дельфийских контролов их оконная ф-я устроена таким образом,
что код обработки сообщения заключён в блок try-except и любое исключение,
возбуждённое в обработчике сообщения (соответственно во всех обработчиках событий,
вызванных из него) будет в любом случае перехвачено.
procedure TWinControl.MainWndProc(var Message: TMessage);
begin
try
try
WindowProc(Message); // здесь происходит реальная обработка сообщенияfinally
FreeDeviceContexts;
FreeMemoryContexts;
end;
exceptApplication.HandleException(Self);end;
end;
Обработано исключение будет в процедуре Application.HandleException (если не будет обработано в пользовательском try-except).
Вот в ней-то и предопределено единственное отличие EAbort от остальных исключений:
в случае EAbort не вызывается окна с сообщением об ошибке.
(Ну, ещё одно отличие — то, что EAbort по умолчанию не дублируется средой)
procedure TApplication.HandleException(Sender: TObject);
begin
if GetCapture <> 0 then SendMessage(GetCapture, WM_CANCELMODE, 0, 0);
if ExceptObject is Exception then
begin
ifnot (ExceptObject is EAbort) then
if Assigned(FOnException) then
FOnException(Sender, Exception(ExceptObject))
else
ShowException(Exception(ExceptObject));
end else
SysUtils.ShowException(ExceptObject, ExceptAddr);
end;
Но если ты, например, изменишь оконную ф-ю твоей кнопки так, код обработки сообщения не был защищён try-except, то вылетишь из программы с сообщением Runtime error XXX
Соответственно, в приведённвх тобой случаях имеем:
1)
procedure TForm1.Button1Click(Sender: TObject);
var
P : tPiper;
begin//P := nil;if P = nil
then Memo1.Lines.Add('1. nil')
else Memo1.Lines.Add('1. NO nil');
P := tPiper.Create; // P не будет изменено, код вылетит в Application.HandleException
// но, окошка с сообщением об ошибке не будет
// Этот код выполнен не будетif P = nil
then Memo1.Lines.Add('2. nil')
else Memo1.Lines.Add('2. NO nil');
Memo1.Lines.Add('Last');
end;
2)
procedure TForm1.Button1Click(Sender: TObject);
var
P : tPiper;
begin//P := nil;if P = nil
then Memo1.Lines.Add('1. nil')
else Memo1.Lines.Add('1. NO nil');
try
P := tPiper.Create; // P не будет изменено, код вылетит в except ниже
// если бы здесь был код, он не был бы выполненexcept// пустой обработчик перехватывает все исключенияend;
// Этот код будет выполнен
// значение P зависит только от того, был ли он обнулён в начале процедурыif P = nil
then Memo1.Lines.Add('2. nil')
else Memo1.Lines.Add('2. NO nil');
Memo1.Lines.Add('Last');
end;
destructor TTest.Destroy();
begin
if Assigned(FObj2) then
FObj2.Free;
if Assigned(FObj1) then
FObj1.Free;
inherited;
end;
Зачем дублировать, то что делает Free, чтобы масло было маслянным?
Первый вариант абсолютно правильный, а второй говорит о серьезных пробелах в
знаниях.
возможно потому, что "Free допустимо к nil вызывать"
(если можно так сказать. как правильно, см. message
выше, цитату из справки)
сообщение я писал, для того чтобы обратить внимание:
что в отличие от переменных(ссылок на
объекты, могущих nil содержать),
для иных ресурсов очень желательно,
освобождать только выделенные
пример, поле fHandle : THandle, до которого LoadLibrary
в конструкторе не дошел, ибо exception.
ты ему в destructor, смело FreeLibrary будешь вызывать ?
а о том и говорю, что неплохо было бы убедиться
что там не мусор и не 0.
параноидально будет так
...
fHandleAllocated : boolean; ;-)
...
if fHandleAllocated then
FreeLibrary(fHandle);
хотя и
if fHandle > 0 then
FreeLibrary(fHandle);
будет достаточно на ERROR_INVALID_HANDLE не нарваться
Posted via RSDN NNTP Server 1.7 "Bedlam"
Только на РСДН помимо ответа на вопрос, можно получить еще список орфографических ошибок и узнать что-то новое из грамматики английского языка (c) http://www.rsdn.ru/forum/cpp/4720035.1.aspx
> Мусор в полях класса не будет. > До входа в контсруктор они нулем инициализируются.
конечно. это у меня привычка еще с Turbo Pascal 5.5
и статических объектов.
мусор бывает лишь при стеке, например в локальных переменных.
что к полям класса, ни коим образом, естестно, не может относиться.
Posted via RSDN NNTP Server 1.7 "Bedlam"
Только на РСДН помимо ответа на вопрос, можно получить еще список орфографических ошибок и узнать что-то новое из грамматики английского языка (c) http://www.rsdn.ru/forum/cpp/4720035.1.aspx
Здравствуйте, Serjio, Вы писали:
>> Мусор в полях класса не будет. >> До входа в контсруктор они нулем инициализируются.
S>конечно. это у меня привычка еще с Turbo Pascal 5.5 S>и статических объектов.
S>мусор бывает лишь при стеке, например в локальных переменных. S>что к полям класса, ни коим образом, естестно, не может относиться.
И еще мусор бывает в "куче". К полям класса это относится непосредственно, но они инициализируются нулями явно в TObject.InitInstance.