Досрочное завершение конструктора класса
От: defrag  
Дата: 15.10.03 16:35
Оценка:
Как отменить создание класса, если в процессе работы конструктора обнаружилось, что какой-нибудь необходимый ресурс занят, или по другой проичине.
Можно ли прямо в конструкторе вызвать метод Free?
Достаточно ли вызвать какой-нибудь exception?
Re: Досрочное завершение конструктора класса
От: VVP Россия http://web.icq.com/whitepages/online?icq=67524421&img=567524421
Дата: 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 Россия http://nebug.chat.ru
Дата: 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 Россия http://nebug.chat.ru
Дата: 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 Россия http://nebug.chat.ru
Дата: 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...
Пока на собственное сообщение не было ответов, его можно удалить.