Досрочное завершение конструктора класса
От: defrag  
Дата: 15.10.03 16:35
Оценка:
Как отменить создание класса, если в процессе работы конструктора обнаружилось, что какой-нибудь необходимый ресурс занят, или по другой проичине.
Можно ли прямо в конструкторе вызвать метод Free?
Достаточно ли вызвать какой-нибудь exception?
Re: Досрочное завершение конструктора класса
От: VVP Россия 67524421
Дата: 15.10.03 16:52
Оценка: +1
Здравствуйте, 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?
Более чем достаточно, кроме того исключение может выкинуть вызванная тобой в конструкторе функция.
Никогда не бойся браться делать то, что делать не умеешь. Помни, ковчег был построен любителем. Профессионалы построили Титаник...
Re[2]: Досрочное завершение конструктора класса
От: PepperLover  
Дата: 16.10.03 06:28
Оценка:
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.
Re[2]: Досрочное завершение конструктора класса
От: rounin  
Дата: 16.10.03 07:20
Оценка:
Здравствуйте, VVP, Вы писали:

Если в конструкторе будет обнаружено, что создать объект невозможно,
в нём следует возбудить исключение.
При этом он будет автоматически уничтожен, как уже указал PepperLover.
(правда, ф-я BeforeDestructor при этом не будет вызвана).
Корректное уничтотожение в деструкторе ещё не созданных объетов-членов
гарантируется тем, что при создании объекта все они были обнулены.
(Поэтому уничтожать объекты-члены следует всегда через Free, которая проверяет
объект на nil).

VVP>Именно поэтому рекомендована следующая запись:
VVP>var
VVP>  vObj: TSomeObject;
VVP>begin
VVP>  vObj:=TSomeObject.Create(...);
VVP>  try
VVP>    { some usefull code }
VVP>    ...
VVP>  finally
VVP>    vObj.Free;
VVP>  end;
VVP>end;
VVP>


Это совсем из другой оперы. Такая запись гарантирует, что если в
some usefull code (а отнюдь не в конструкторе) произойдёт исключение,
то объект будет уничтожен. Если исключение будет возбуждено в
конструкторе, то никакой код после него вызван не будет, а освободится
объект автоматически.
Re[3]: Досрочное завершение конструктора класса
От: PepperLover  
Дата: 16.10.03 07:25
Оценка:
R>Если в конструкторе будет обнаружено, что создать объект невозможно,
R>в нём следует возбудить исключение.
И к этому следует добавить, что (может быть),
если надо просто вернуть после конструктора nil,
без вываливания из программы и заключения его в try...end;
процедуру Abort; (Raises a silent exception. unit SysUtils)

.
Re[4]: Досрочное завершение конструктора класса
От: rounin  
Дата: 16.10.03 10:07
Оценка:
Здравствуйте, PepperLover, Вы писали:

PL>И к этому следует добавить, что (может быть),

PL>если надо просто вернуть после конструктора nil,
PL>без вываливания из программы и заключения его в try...end;
PL>процедуру Abort; (Raises a silent exception. unit SysUtils)

При этом конструктор вовсе не вернёт нил.
Будет всё тоже самое "вываливание из программы", что и при обычном исключении,
только не выскочит окошко с сообщением об ошибке.
Re[3]: Досрочное завершение конструктора класса
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 16.10.03 10:23
Оценка: +1
Здравствуйте, 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 выглядит примерно так:

procedure SomeProc;
begin
  try
    FTest := TTest.Create;
  except
    FTest.Free;
    raise;
  end;
  try
    // .....
  finally
    FTest.Free;
  end;
end;


Если надо проверять создание объекта, то лучше использовать фабричный метод:

function CreateTest: TTest;
begin
  try
    Result := TTest.Create;
  except
    Result := nil
  end;
end;
Re[4]: Досрочное завершение конструктора класса
От: Serjio Россия  
Дата: 17.10.03 05:45
Оценка:
> В случае возникновения исключительной ситуации в конструкторе,
автоматически вызывается его деструктор,
> так что отпадает проблема заботиться в том, что проинициализировано, а что
нет.

> destructor TTest.Destroy();
> begin
>  FObj2.Free;
>  FObj1.Free;
>  inherited;
> end;


вместо

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
Автор: ZOI4
Дата: 28.04.12
Re[5]: Досрочное завершение конструктора класса
От: PepperLover  
Дата: 17.10.03 06:53
Оценка:
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
Re[6]: Досрочное завершение конструктора класса
От: rounin  
Дата: 17.10.03 08:36
Оценка: 20 (2) +1
Здравствуйте, PepperLover, Вы писали:

В случае любого исключения происходит "вываливание" до первого (самого внутреннего
из вложенных) обработчика данного исключения.
Код вне блока try-except, в котором определён обработчик
данного исключения, будет выполнен, как будто никакого исключения не было.

Если же обработчика исключения не найдётся, происходит аварийное завершение программы.
Никаких исключений для EAbort здесь нет.

В случае стандартных дельфийских контролов их оконная ф-я устроена таким образом,
что код обработки сообщения заключён в блок try-except и любое исключение,
возбуждённое в обработчике сообщения (соответственно во всех обработчиках событий,
вызванных из него) будет в любом случае перехвачено.

procedure TWinControl.MainWndProc(var Message: TMessage);
begin
  try
    try
      WindowProc(Message); // здесь происходит реальная обработка сообщения
    finally
      FreeDeviceContexts;
      FreeMemoryContexts;
    end;
  except
    Application.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
    if not (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;
Re[5]: Досрочное завершение конструктора класса
От: Anatoly Podgoretsky Эстония http://www.podgoretsky.com
Дата: 18.10.03 10:11
Оценка:
> destructor TTest.Destroy();
> begin
> FObj2.Free;
> FObj1.Free;
> inherited;
> end;

вместо

destructor TTest.Destroy();
begin
if Assigned(FObj2) then
FObj2.Free;
if Assigned(FObj1) then
FObj1.Free;
inherited;
end;

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

--
Anatoly Podgoretsky
http://podgoretsky.com
Posted via RSDN NNTP Server 1.7 "Bedlam"
Re[6]: Досрочное завершение конструктора класса
От: Serjio Россия  
Дата: 20.10.03 06:22
Оценка:
а можно попросить, сообщения дочитывать до конца

FObj2.Free;

вместо
if Assigned(FObj2) then
  FObj2.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
Автор: ZOI4
Дата: 28.04.12
Re[7]: Досрочное завершение конструктора класса
От: AlexVinS Россия  
Дата: 20.10.03 07:54
Оценка:
Здравствуйте, Serjio, Вы писали:



S>а о том и говорю, что неплохо было бы убедиться

S>что там не мусор и не 0.

Мусор в полях класса не будет. До входа в контсруктор они нулем инициализируются.


Умный человек знает не многое, но нужное
Re[8]: Досрочное завершение конструктора класса
От: Serjio Россия  
Дата: 20.10.03 08:16
Оценка:
> Мусор в полях класса не будет.
> До входа в контсруктор они нулем инициализируются.

конечно. это у меня привычка еще с Turbo Pascal 5.5
и статических объектов.

мусор бывает лишь при стеке, например в локальных переменных.
что к полям класса, ни коим образом, естестно, не может относиться.
Posted via RSDN NNTP Server 1.7 "Bedlam"
Только на РСДН помимо ответа на вопрос, можно получить еще список орфографических ошибок и узнать что-то новое из грамматики английского языка (c) http://www.rsdn.ru/forum/cpp/4720035.1.aspx
Автор: ZOI4
Дата: 28.04.12
Re[9]: Досрочное завершение конструктора класса
От: AlexVinS Россия  
Дата: 20.10.03 08:47
Оценка:
Здравствуйте, Serjio, Вы писали:

>> Мусор в полях класса не будет.

>> До входа в контсруктор они нулем инициализируются.

S>конечно. это у меня привычка еще с Turbo Pascal 5.5

S>и статических объектов.

S>мусор бывает лишь при стеке, например в локальных переменных.

S>что к полям класса, ни коим образом, естестно, не может относиться.

И еще мусор бывает в "куче". К полям класса это относится непосредственно, но они инициализируются нулями явно в TObject.InitInstance.


Умный человек знает не многое, но нужное
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.