Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 2010.08.27;
Скачать: [xml.tar.bz2];

Вниз

А я и не знал...   Найти похожие ветки 

 
Дмитрий С ©   (2010-03-05 03:34) [0]

... что если поднять исключение в конструкторе, то автоматически вызовется деструктор destroy.


 
Дмитрий С ©   (2010-03-05 03:35) [1]

Теперь понятно зачем во Free проверка на Self <> nil


 
Lamer@fools.ua ©   (2010-03-05 09:18) [2]

>>Дмитрий С ©   (05.03.10 03:34)

>.. что если поднять исключение в конструкторе, то автоматически вызовется деструктор destroy.
"Как много нам открытий чудных..."

>Теперь понятно зачем во Free проверка на Self <> nil
Неправильно Вам понятно :-)


 
KSergey ©   (2010-03-05 09:34) [3]

Да, к сожалению этот момент как-то обойден вниманием в книгах совершенно.
При том, что момент просто фантастической замечательности, чисто дельфовый.


 
oxffff ©   (2010-03-05 09:59) [4]


> Дмитрий С ©   (05.03.10 03:35) [1]
> Теперь понятно зачем во Free проверка на Self <> nil


Выход по исключению из конструктора находится за пределами операции присваивания. Поэтому воздействия на переменную-приемник никакого.


 
@!!ex ©   (2010-03-05 10:00) [5]

> [3] KSergey ©   (05.03.10 09:34)
> Да, к сожалению этот момент как-то обойден вниманием в книгах
> совершенно.

По одной простой причине:
Только в дельфи можно вызывать исключения в конструкторе.
И вообще, конструктор не должен делать ничего такого, что может вызвать исключение.
Для этого есть другие методы, например Init и LoadFromFile...
По хорошему конструктор должен только инициализировать объект значениями по умолчению.
А настраиватся объект должен отдельно.
Хотя я и сам в Дельфи частенько делаю лишнее в конструкторе.


 
Владислав ©   (2010-03-05 10:10) [6]


> @!!ex ©   (05.03.10 10:00) [5]


А мужики то не знают...


 
@!!ex ©   (2010-03-05 10:11) [7]

> [6] Владислав ©   (05.03.10 10:10)

Это точно.
Дельфи способствует говнокоду, встречал код написанный !мастерами! с вещами, за которые эти же мастера обычно новичков опускают. :)


 
brother ©   (2010-03-05 10:19) [8]

> новичков опускают. :)

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


 
@!!ex ©   (2010-03-05 10:21) [9]

> [8] brother ©   (05.03.10 10:19)

согласен. не имел ввиду зоновский смысл. :)


 
KilkennyCat ©   (2010-03-05 10:45) [10]


> Дельфи способствует говнокоду

неверно. Делфи тут нипричем, говнокод можно и в машкодах написать, и в любом другом языке\инструменте


 
Игорь Шевченко ©   (2010-03-05 10:55) [11]


> Дельфи способствует говнокоду


а народ вот другие языки тут не любит, говорит, что паскаль ваще рулез. странно, да ?


 
DrPass ©   (2010-03-05 10:59) [12]


> @!!ex ©   (05.03.10 10:11) [7]
> > [6] Владислав ©   (05.03.10 10:10)Это точно.Дельфи способствует
> говнокоду

Странный у тебя образ мышления.

> И вообще, конструктор не должен делать ничего такого, что
> может вызвать исключение.Для этого есть другие методы, например
> Init и LoadFromFile...По хорошему конструктор должен только
> инициализировать объект значениями по умолчению.А настраиватся
> объект должен отдельно.

...и понятия о правильной архитектуре


 
Ega23 ©   (2010-03-05 10:59) [13]


> Теперь понятно зачем во Free проверка на Self <> nil


Разницу между Free и Destroy осчусчаешь?  :)


 
oxffff ©   (2010-03-05 11:05) [14]


> @!!ex ©   (05.03.10 10:11) [7]
> > [6] Владислав ©   (05.03.10 10:10)
>
> Это точно.
> Дельфи способствует говнокоду,


Ощущается влияние gamedev.ru на тебя.


 
@!!ex ©   (2010-03-05 11:05) [15]

> [10] KilkennyCat ©   (05.03.10 10:45)

Ну да. Глобальные формы пользователь сам создает, а не Delphi это делает. :))


> [12] DrPass ©   (05.03.10 10:59)
> ...и понятия о правильной архитектуре

Если что - VCL так сделан.


 
@!!ex ©   (2010-03-05 11:07) [16]

> [14] oxffff ©   (05.03.10 11:05)

Не. gamedev.ru тут мало влияния оказывает.
Скорее Шлее и МакКоннелл


 
oxffff ©   (2010-03-05 11:07) [17]


> @!!ex ©   (05.03.10 11:05) [15]
> > [10] KilkennyCat ©   (05.03.10 10:45)
>
> Ну да. Глобальные формы пользователь сам создает, а не Delphi
> это делает. :))


Это делает программист.


 
@!!ex ©   (2010-03-05 11:10) [18]

> [17] oxffff ©   (05.03.10 11:07)

:))


 
DVM ©   (2010-03-05 11:14) [19]


> @!!ex ©   (05.03.10 10:00) [5]


> Только в дельфи можно вызывать исключения в конструкторе.

не только, в с++ тоже можно.


 
jack128_   (2010-03-05 11:18) [20]


> >Теперь понятно зачем во Free проверка на Self <> nil
> Неправильно Вам понятно :-)


>
> Выход по исключению из конструктора находится за пределами
> операции присваивания. Поэтому воздействия на переменную-
> приемник никакого.


вообще Free имеет непосредственное отношение к сабжевому вопросу.
Free было специально введено, для такого сценария работы с объектами:

type
 TMyObj = class
   FInnerObj: TObject;
 end;

constructor TMyObj.Create
begin
 DoWork(); // тут туча кода, который может кинуть исключение

 FInnerObj := TObject.Create;
end;

destructor TMyObj.Destroy;
begin
 FInnerObj.Free;
end;


 
@!!ex ©   (2010-03-05 11:21) [21]

> [19] DVM ©   (05.03.10 11:14)

http://www.cyberguru.ru/programming/cpp/cpp-programming-rules2-page73.html


 
DVM ©   (2010-03-05 11:24) [22]


> @!!ex ©   (05.03.10 11:21) [21]

Ненадежно и нельзя - это же разные вещи. То, что такой подход не самый лучший - очевидно.


 
@!!ex ©   (2010-03-05 11:27) [23]

> [22] DVM ©   (05.03.10 11:24)

Можно все.
Нельзя с точки зрения хорошего программирования. Вроде разговор именно в этом контексте.


 
Игорь Шевченко ©   (2010-03-05 11:34) [24]


> Free было специально введено, для такого сценария работы
> с объектами:


Источник ?


 
Piter ©   (2010-03-05 11:58) [25]

Дмитрий С ©   (05.03.10 3:34)
... что если поднять исключение в конструкторе, то автоматически вызовется деструктор destroy.


что очень логично. И это единственный нормальный способ прервать создание объекта.

@!!ex ©   (05.03.10 10:00) [5]
И вообще, конструктор не должен делать ничего такого, что может вызвать исключение.
Для этого есть другие методы, например Init и LoadFromFile...
По хорошему конструктор должен только инициализировать объект значениями по умолчению.
А настраиватся объект должен отдельно.


кто это сказал?

На мой взгляд - конструктор может делать все, что нужно для существования объекта. И если в конструкторе обнаружится, что существование экземпляра данного класса невозможно или он априори не сможет выполнять свою работу - логично сделать исключение в конструкторе.


 
KSergey ©   (2010-03-05 12:54) [26]

> Игорь Шевченко ©   (05.03.10 11:34) [24]
> > Free было специально введено, для такого сценария работы с объектами:
> Источник ?

А что, есть возражения??
По-моему, как раз правильно написано, все в дельфи как раз и заточено именно под такой сценарий, включая автоматический вызов десруктора при исключениях в конструкторе.
Если это не так - просьба пояснить что и где именно.


 
Kerk ©   (2010-03-05 12:59) [27]


> @!!ex ©   (05.03.10 10:00) [5]
>
> По хорошему конструктор должен только инициализировать объект
> значениями по умолчению.
> А настраиватся объект должен отдельно.

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


 
@!!ex ©   (2010-03-05 13:03) [28]

> [27] Kerk ©   (05.03.10 12:59)

Тебя же не удивляет, что, например, TidTCPServer
все настройки идут после конструктора, а конструктор делает только базовую инициализацию.
Еще раз:
весь(во всяком случае большая часть, весь я его просто не видел) VCL сделан по такому принципу.


 
Kerk ©   (2010-03-05 13:06) [29]


> @!!ex ©   (05.03.10 13:03) [28]
> Тебя же не удивляет, что, например, TidTCPServer
> все настройки идут после конструктора, а конструктор делает
> только базовую инициализацию.

Не удивляет, но часто такой подход ничего кроме потенциальных проблем в себе не несет.


 
TUser ©   (2010-03-05 13:10) [30]

В шарпе можно исключения в конструкторе


 
KSergey ©   (2010-03-05 13:19) [31]

Kerk ©   (05.03.10 13:06) [29]
@!!ex ©   (05.03.10 13:03) [28]

По-моему, здесь надо четко различать разные языки.
Дельфи вполне заточен под исключения в конструкторе благодаря автовызову деструктора.
С++ - не заточен нифига, хотя руками на нем это сделать и можно, но только надеясь на свои руки и следование определенным шаблонам построения кода, пользоваться определенными классами, заточенными на автовызов деструктора при покидании области видимости.
С другой строны - просто не надо в С++ использовать указатели; автовызов деструктора есть и там, просто он в других ситуациях выполняется.


 
KSergey ©   (2010-03-05 13:21) [32]

> KSergey ©   (05.03.10 13:19) [31]
> С другой строны - просто не надо в С++ использовать указатели;

здесь имелось ввиду, что не надо использовать лишь указатели для хранения ссылок на объекты. Надо создавать полноценный объект, умеющий в деструкторе освобождать вверенный ему на хранение объект.


 
Ega23 ©   (2010-03-05 13:22) [33]


> В шарпе можно исключения в конструкторе


Дык и в Delphi можно, никто не запрещает. В VCL где-то видел. Сам использовал 1 раз в жизни, и то только потому, что иначе очень геморно получалось.


 
Игорь Шевченко ©   (2010-03-05 13:29) [34]

KSergey ©   (05.03.10 12:54) [26]


> А что, есть возражения??


Есть пожелания указывать источник, что TObject.Free был специально введен для подобных случаев.

Поскольку довольно часто встречаю конструкции вида:

destructor TFoo.Destroy;
...
if Assigned(FBar) then
  FBar.Free;
...
end;


или

destructor TFoo.Destroy;
...
if Assigned(FBar) then
  FreeAndNil(FBar);
...
end;


 
KSergey ©   (2010-03-05 13:36) [35]

> Игорь Шевченко ©   (05.03.10 13:29) [34]
> Поскольку довольно часто встречаю конструкции вида:

Я тоже могу привести примеры того, что "много где встречается". Плоховатого кода от незнания - его много, и что из того?
Источник, к стати, вами тоже не указан.

Реализация автоматического обнуления выделенной памяти под объект, автовызов деструктора, введение спец. метода Free с проверкой на nil - на мой взгляд явно указывает на то, что все это в комплексе сделано отнюдь не случайно, а для реализации именно такого стандартного кода, который приведен у jack128_   [20] и который мы повсеместно встречаем в VCL. Хотя не исключаю, что где-то в VCL есть и приведенный в [34] код, но разве это указывает на то, что в [20] написано что-либо некорректно?


 
DVM ©   (2010-03-05 13:39) [36]


> @!!ex ©   (05.03.10 13:03) [28]


> Тебя же не удивляет, что, например, TidTCPServer
> все настройки идут после конструктора, а конструктор делает
> только базовую инициализацию.

Это сделано по другой причине. Это компонент в первую очередь и должна быть возможность настройки менять в DesignTime потому так и сделано.


 
@!!ex ©   (2010-03-05 14:02) [37]

> [36] DVM ©   (05.03.10 13:39)

StringList тоже надо в DesignTime менять? ;)


 
Ega23 ©   (2010-03-05 14:08) [38]


> StringList тоже надо в DesignTime менять? ;)


А что с ним не так?


 
GrayFace ©   (2010-03-05 14:08) [39]

Piter, опередил :)
В С++ и других языках с убогими конструкторами я бы с процитированным согласился, частично. Там полноценная инициализация вообще невозможна в конструкторе - виртуальную функцию, перегруженную в наследнике, не вызвать, до конструктора предка что-то исполнить проблематично. Про исключения не знал.
А приминительно к Дельфи эта религиозная заповедь бессмысленна.


 
Kerk ©   (2010-03-05 14:09) [40]


> @!!ex ©   (05.03.10 14:02) [37]

Плохой пример. TStringList сразу после создания готов к работе. Речь о случаях, когда классу требуется дополнительная инициализация.


 
Ega23 ©   (2010-03-05 14:12) [41]

Кстати, @!!ex ©, как ты тогда это прокомментируешь?
constructor TThread.Create(CreateSuspended: Boolean);

:)


 
Piter ©   (2010-03-05 14:15) [42]

@!!ex ©   (05.03.10 13:03) [28]
Тебя же не удивляет, что, например, TidTCPServer
все настройки идут после конструктора, а конструктор делает только базовую инициализацию.


это всего лишь подход. Который может быть иным.
Почему один правильнее другого - ты не уточняешь.

Назови плюсы твоего подхода, послушаем.


 
GrayFace ©   (2010-03-05 14:43) [43]

TMemIniFile.Create(const FileName: string) сразу открывает файл и весь его загружает. Если файл существует, но его не удасться прочесть, будет исключение.
@!!ex, что скажешь? Как бы сделал этот класс ты?


 
oxffff ©   (2010-03-05 14:46) [44]



> jack128_   (05.03.10 11:18) [20]
>
> > >Теперь понятно зачем во Free проверка на Self <> nil
> > Неправильно Вам понятно :-)
>
>
> >
> > Выход по исключению из конструктора находится за пределами
>
> > операции присваивания. Поэтому воздействия на переменную-
>
> > приемник никакого.
>
>
> вообще Free имеет непосредственное отношение к сабжевому
> вопросу.
> Free было специально введено, для такого сценария работы
> с объектами:
>
> type
>  TMyObj = class
>    FInnerObj: TObject;
>  end;
>
> constructor TMyObj.Create
> begin
>  DoWork(); // тут туча кода, который может кинуть исключение
>
>  FInnerObj := TObject.Create;
> end;


1. А где гарантии, что FInnerObj равен nil, для того чтобы декструктор штатно отработвал. Newinstance что обязательно установить 0?
2. Где гарантии что в деструкторе ссылка не прибитая?

:)


 
Медвежонок Пятачок ©   (2010-03-05 14:48) [45]

И вообще, конструктор не должен делать ничего такого, что может вызвать исключение.

Банально кончилась память.
Обломавшийся ГетМем конечно не поднимет исключения.
А конструктору тоже молчать?

:)


 
DVM ©   (2010-03-05 14:50) [46]


> TMemIniFile.Create(const FileName: string)

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


 
Anatoly Podgoretsky ©   (2010-03-05 14:54) [47]

> oxffff  (05.03.2010 14:46:44)  [44]

Гарантия в механизме исключений. Если будет исключение, то значение не будет присвоено и Free не будет пытаться уничтожать.


 
oxffff ©   (2010-03-05 14:57) [48]


> Anatoly Podgoretsky ©   (05.03.10 14:54) [47]
> > oxffff  (05.03.2010 14:46:44)  [44]
>
> Гарантия в механизме исключений. Если будет исключение,
> то значение не будет присвоено и Free не будет пытаться
> уничтожать.


Берем перекрываем аллокатор newinstance без очистки на 0 для неуправляемых объектов. Получаем исключение на free. Гарантий нет!!!


 
Anatoly Podgoretsky ©   (2010-03-05 15:05) [49]

> oxffff  (05.03.2010 14:57:48)  [48]

Ты про стеклянный член слышал?


 
oxffff ©   (2010-03-05 15:05) [50]


> Anatoly Podgoretsky ©   (05.03.10 15:05) [49]
> > oxffff  (05.03.2010 14:57:48)  [48]
>
> Ты про стеклянный член слышал?


Не я просто умные книжки читаю. Слышали про такие?


 
Медвежонок Пятачок ©   (2010-03-05 15:21) [51]

по моему все забыли для чего существуют исключения.

это не ошибки, а элегантный способ избавится от многоэтажных ифов


 
Kerk ©   (2010-03-05 15:23) [52]


> Медвежонок Пятачок ©   (05.03.10 15:21) [51]

Ну да. Исключение - это совершенно штатная ситуация.


 
Дмитрий С ©   (2010-03-05 15:27) [53]


> Выход по исключению из конструктора находится за пределами
> операции присваивания. Поэтому воздействия на переменную-
> приемник никакого.

Эта проверка во Free по-моему задумана вот для чего:

TSomeObject = class
FList1, FList2: TList;
constructor Create;
destructor Destroy; override;
end;

constructor TSomeObject.Create;
begin
 FList1 := TList.Create; // Предположим в этой строке поднялось исключение.
 FList2 := TList.Create;
end;

destructor TSomeObject.Destroy;
begin
 FList1.Free;
 FList2.Free; // Тогда в этой строке исключения не будет, т.к. метод FList2.Free проверит FList2 на nil перед вызовом его деструктора.
end;

А вы о чем подумали?:)


> Ega23 ©   (05.03.10 10:59) [13]
> Разницу между Free и Destroy осчусчаешь?  :)

Конечно :)


 
@!!ex ©   (2010-03-05 15:29) [54]

> [43] GrayFace ©   (05.03.10 14:43)

Неудачный пример.
В первую очередь потому, что отличается от общего стиля остальной бибилиотеки, что плохо.
Если делать в общем стиле, нужно делать через LoadFromFile.
Я бы сделал через LoadFromFile,SaveToFile.
Еще раз:
Если бы у меня возникла необходимость сделать что-то в Create, я бы это сделал. И в принципе сейчас делаю. Но не считаю что это правильно. Это проще.


 
KSergey ©   (2010-03-05 15:32) [55]

> oxffff ©   (05.03.10 14:57) [48]
> Берем перекрываем аллокатор newinstance без очистки на 0
> для неуправляемых объектов. Получаем исключение на free.
>  Гарантий нет!!!

Речь про штатное поведение дельфи.
Если сделал говно - не надо другим его тыкать.


 
Dimka Maslov ©   (2010-03-05 15:33) [56]


> ... что если поднять исключение в конструкторе, то автоматически
> вызовется деструктор destroy.


А ещё, оказывается, можно в конструкторе вызывать виртуальные методы. И многое другое, что написано в анналах и нельзя в других языках. Надо только прочитать и уяснить.


> не только, в с++ тоже можно.

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


 
KSergey ©   (2010-03-05 15:34) [57]

> Дмитрий С ©   (05.03.10 15:27) [53]
> Эта проверка во Free по-моему задумана вот для чего:

Это все уже написано в [20]
Зачем писать еще раз?


 
Дмитрий С ©   (2010-03-05 15:36) [58]


> jack128_   (05.03.10 11:18) [20]

Не успел прочитать, ответил сам:)


> destructor TFoo.Destroy;
> ...
> if Assigned(FBar) then
>   FBar.Free;
> ...
> end;
>
> или
>
> destructor TFoo.Destroy;
> ...
> if Assigned(FBar) then
>   FreeAndNil(FBar);
> ...
> end;

Тут IF скорее всего добавлен для понятности. Что при вызове деструктора, логикой так заложено, что FBar может быть nil.
Ну, например, если FBar с самого начала существования объекта не нужен, а инициализируется впоследствии. Как-то так.


 
Kerk ©   (2010-03-05 15:39) [59]


> @!!ex ©   (05.03.10 15:29) [54]
>
> > [43] GrayFace ©   (05.03.10 14:43)
>
> Неудачный пример.
> В первую очередь потому, что отличается от общего стиля
> остальной бибилиотеки, что плохо.

Причем тут библиотека вообще? VCL - эталон?


 
@!!ex ©   (2010-03-05 15:48) [60]

> [59] Kerk ©   (05.03.10 15:39)
>
> > @!!ex ©   (05.03.10 15:29) [54]
> >
> > > [43] GrayFace ©   (05.03.10 14:43)
> >
> > Неудачный пример.
> > В первую очередь потому, что отличается от общего стиля
>
> > остальной бибилиотеки, что плохо.
>
> Причем тут библиотека вообще? VCL - эталон?

Delphi без VCL имеет мало ценности.
Писать в разных стилях в пределах одного проекта - плохо.


 
oxffff ©   (2010-03-05 15:50) [61]


> KSergey ©   (05.03.10 15:32) [55]
> > oxffff ©   (05.03.10 14:57) [48]
> > Берем перекрываем аллокатор newinstance без очистки на
> 0
> > для неуправляемых объектов. Получаем исключение на free.
>
> >  Гарантий нет!!!
>
> Речь про штатное поведение дельфи.
> Если сделал говно - не надо другим его тыкать.


Причем здесь говно?
В записи активации в стеке инициализируется только управляемые объекты.
В остальных мусор.


 
Игорь Шевченко ©   (2010-03-05 15:58) [62]

KSergey ©   (05.03.10 15:32) [55]


> Речь про штатное поведение дельфи.


Штатным поведением Delphi запрещено перекрывать NewInstance ?


 
Alkid ©   (2010-03-05 16:01) [63]

Народ, хорош на C# и С++ наезжать! Выброс исключение в конструкторе у них - совершенно нормальная идиома, против которой нет никаких законов.

Более того, в C++ на этот счет все устроено достаточно логично: при выбросе исключения из конструктора вызываются деструкторы всех УЖЕ СКОНСТРУИРОВАННЫХ подобъектов.

Т.е:

class A
{
public:
  A();
 ~A();
}

class A2
{
public:
 A2();
 ~A2();
}

class B : public A
{
public:
 B () { throw 0; }
 ~B();
}

...

B b();

Цепочка вызовов будет: A(), A2(), B() с выбросом, ~A2(), ~A().
И никаких утечек!
Читайте Стандарт и учите матчасть.
Или не критикуйте то, чего не знаете!


 
Alkid ©   (2010-03-05 16:01) [64]

Поправка к предыдущему сообщению:

...
class B : public A, public A2
...


 
Alkid ©   (2010-03-05 16:03) [65]

А касаемо вызова виртуальной функции в конструкторе - господа, как вы намереваетесь вызывать функцию для объекта, который еще не создан, а?


 
Игорь Шевченко ©   (2010-03-05 16:07) [66]


> А касаемо вызова виртуальной функции в конструкторе - господа,
>  как вы намереваетесь вызывать функцию для объекта, который
> еще не создан, а?


Конструктор вызывается, когда объект уже создан, но не "сконструирован" :)
Вызвать виртуальную функцию в этот момент ничего не мешает, таблица виртуальных функций уже находится на нужном месте.


 
@!!ex ©   (2010-03-05 16:13) [67]

> [63] Alkid ©   (05.03.10 16:01)

Вы читали ссылку которую я давал или просто так написать решили?


 
Alkid ©   (2010-03-05 16:14) [68]


> Игорь Шевченко ©   (05.03.10 16:07) [66]

Я о другом. Концепция класса с конструкторами и деструкторами дает программисту определенные инварианты, на которые он может опираться. Например, что вызов метода объекта может быть произведен только после того, как объект будет сконструирован, т.е. когда конструктор завершит свою работу.

Рассмотрим пример:

class A
{
public:
 A() { F(); }
 virtual void F() { printf("A::A()");}
}

class B : public A
{
public:
 B() { message = "Hello, World!"; }
 virtual F() { printf("%s \n", message); }

private:
 char* message;
}

Согласно стандарту в A::A() будет вызван метод A::F(), что сохраняет озвученный выше инвариант. Если бы в A::A() вызывалась самая перегруженная реализация, т.е. B::F(), то мы бы имели вызов метода класса B до вызова конструктора класса. В приведенном примере это будет означать стрельбу по нулевому указателю, т.е. AV.

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


 
Alkid ©   (2010-03-05 16:16) [69]


> @!!ex ©   (05.03.10 16:13) [67]

Искренне пытался, но она у меня не открылась.


 
@!!ex ©   (2010-03-05 16:20) [70]

> [69] Alkid ©   (05.03.10 16:16)

Суть ссылки:
По стандарту исключение в конструкторе допустимо и не приведет к ошибка.
Но далеко не все современные распространенные компиляторы это правильно обрабатывают.

P.S.
Про то, что не существуют компилятора полностью реализцющего стандарт С++ вы наверняка в курсе.


 
tesseract ©   (2010-03-05 16:22) [71]


> Про то, что не существуют компилятора полностью реализцющего
> стандарт С++ вы наверняка в курсе.


Ну работать-то нужно :-D


 
DVM ©   (2010-03-05 16:24) [72]


> @!!ex ©   (05.03.10 16:20) [70]


> Но далеко не все современные распространенные компиляторы
> это правильно обрабатывают.

У gcc с этим точно нет проблем.


 
tesseract ©   (2010-03-05 16:37) [73]


> У gcc с этим точно нет проблем.


Может всё-таки g++ ? Gcc не совсем компилятор, а скорее фронтэнд.


 
Игорь Шевченко ©   (2010-03-05 16:41) [74]

Alkid ©   (05.03.10 16:14) [68]


> Согласно стандарту в A::A() будет вызван метод A::F()


в С++ порядком вызова конструкторов разве можно управлять ? А в Delphi можно.


 
@!!ex ©   (2010-03-05 16:42) [75]

> [72] DVM ©   (05.03.10 16:24)

Судя по статье у MS с этим проблема есть.
Проверять пока повода не было.


 
Дмитрий С ©   (2010-03-05 16:44) [76]

Хотел задать вопрос: "Для чего нужна эта таблица виртуальных методов? А точнее почему она создается в run-time? Разве компилятор при компиляции не может определить где находится код того или иного метода?".

Пока писал - сам понял. Заодно понял откуда взялось название "виртуальный". Сегодня день открытий прям.


 
Игорь Шевченко ©   (2010-03-05 16:47) [77]


> А точнее почему она создается в run-time?


Она не создается в ран-тайм. А вот присвоение ее конкретному экземпляру объекта происходит именно в ран-тайм.


 
Alkid ©   (2010-03-05 16:51) [78]


> @!!ex ©   (05.03.10 16:20) [70]

По ссылке бред написан. Не читайте его.

Вот пример:

#include <tchar.h>
#include <stdio.h>
#include <stdlib.h>

class B
{
public:
   B()
   {
       printf("B::B()\n");
   }
   ~B()
   {
       printf("B::~B()\n");
   }
};

class A : public B
{
public:
   A()
   {
       printf("A::A()\n");
       throw 0;
   }

   ~A()
   {
       printf("A::~A(), Should never get here.\n");
   }

   void * operator new(size_t size)
   {
       printf("Allocating\n");
       return malloc(size);
   }

   void operator delete(void* p)
   {
       printf("Deallocating\n");
       free(p);
   }
};

int _tmain(int argc, _TCHAR* argv[])
{
   printf("Starting.\n");
   try
   {
       A* a = new A();
       printf("Like a normal work. Should never get here\n");
   }
   catch(...)
   {
       printf("Error!\n");
       return 1;
   }
   printf("Finishing like a normal work. Should never get here.\n");
return 0;
}


Вот выход из примера:

Starting.
Allocating
B::B()
A::A()
B::~B()
Deallocating
Error!

Все нормально создается, уничтожается и освобождается.


 
Alkid ©   (2010-03-05 16:55) [79]


> Игорь Шевченко ©   (05.03.10 16:41) [74]
> в С++ порядком вызова конструкторов разве можно управлять? А в Delphi можно.

Вот потому-то и нельзя вызывать виртуальные методы из базовых классов, что нельзя управлять порядком вызова конструкторов. Если понимать правила С++, то все становится достаточно логично и очевидно.

Кстати, это уже отдельная тема - порядок вызова конструкторов это тоже инвариант, который не следует нарушать. То, что дельфи позволяет вызывать конструктор базового класса произвольным образом, или даже вообще его не вызывать - это глюк.


 
Игорь Шевченко ©   (2010-03-05 16:57) [80]

Alkid ©   (05.03.10 16:55) [79]


> Вот потому-то и нельзя вызывать виртуальные методы из базовых
> классов


Вот потому и в стандарте приняли, что нельзя управлять порядком вызова.


> То, что дельфи позволяет вызывать конструктор базового класса
> произвольным образом, или даже вообще его не вызывать -
> это глюк.


Это не глюк, а свойство языка. Точно также можно сказать, что слово friend в C++ это глюк, потому что в Delphi его нет (и близкого понятия - тоже).


 
Anatoly Podgoretsky ©   (2010-03-05 16:59) [81]

> Alkid  (05.03.2010 16:55:19)  [79]

Суровые вы приплюснутые ребята, чуть что не по вашему, так глюк


 
Alkid ©   (2010-03-05 17:03) [82]


> Игорь Шевченко ©   (05.03.10 16:57) [80]
> Вот потому и в стандарте приняли, что нельзя управлять порядком
> вызова.

Нет. Если брать исторический аспект, описанный в "дизайне и эволюции", то первопричиной там фигурирует именно желание обеспечить сохранение инвариантов, которые язык предоставляет программисту, относительно классов. А именно, что никакой метод не будет вызван до отработки конструктора.

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


> Это не глюк, а свойство языка. Точно также можно сказать,
>  что слово friend в C++ это глюк, потому что в Delphi его
> нет (и близкого понятия - тоже).

В Дельфи все классы пределах юнита френды по умолчанию, так что близкое понятие таки есть :)

Что касается определения "глюк".
Выражусь иначе - я считаю, что это недостаток дизайна языка.


 
Alkid ©   (2010-03-05 17:04) [83]


> Anatoly Podgoretsky ©   (05.03.10 16:59) [81]

Вай, зачем ярлыки вешать, дарагой!

А и приплюснутый, и приделфлённый, и пришарпованный :)
А если брать все языки, которые я изучал для расширения кругозора, то вообще безродный космополит получаюсь :)


 
GrayFace ©   (2010-03-05 17:05) [84]

oxffff ©   (05.03.10 14:57) [48]
Берем перекрываем аллокатор newinstance без очистки на 0 для неуправляемых объектов. Получаем исключение на free. Гарантий нет!!!

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

@!!ex ©   (05.03.10 15:29) [54]
Неудачный пример.
В первую очередь потому, что отличается от общего стиля остальной бибилиотеки, что плохо.
Если делать в общем стиле, нужно делать через LoadFromFile.
Я бы сделал через LoadFromFile,SaveToFile.

Неудачный для тебя :)
То, что такой конструктор есть - это правильно, т.к позволяет писать
with TMemIniFile.Create("options.ini") do
 try
   ...
 finally
   Free;
 end;

Более гибко было бы добавить конструктор без параметров и сделать изменяемым FileName, но конструктор с параметром оставить - от него только польза.

Alkid ©   (05.03.10 16:14) [68]
Согласно стандарту в A::A() будет вызван метод A::F(), что сохраняет озвученный выше инвариант. Если бы в A::A() вызывалась самая перегруженная реализация, т.е. B::F(), то мы бы имели вызов метода класса B до вызова конструктора класса. В приведенном примере это будет означать стрельбу по нулевому указателю, т.е. AV.

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


В Дельфи:
constructor B.Create();
begin
 message:= "Hello, World!";
 inherited Create();
end;

Вот это - действительно продуманно.


 
Alkid ©   (2010-03-05 17:08) [85]


> GrayFace ©   (05.03.10 17:05) [84]
> Вот это - действительно продуманно.

Нет. Я уже выше написал - в таком раскладе есть возможность вызывать методы базового класса до его конструирования, что недопустимо.


 
GrayFace ©   (2010-03-05 17:14) [86]

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


 
Alkid ©   (2010-03-05 17:16) [87]


> GrayFace ©   (05.03.10 17:14) [86]

Когда вообще допустимо пользоваться экземпляром ДО его конструирования?


 
Игорь Шевченко ©   (2010-03-05 17:18) [88]

Alkid ©   (05.03.10 17:03) [82]


> то первопричиной там фигурирует именно желание обеспечить
> сохранение инвариантов, которые язык предоставляет программисту,
>  относительно классов


В Delphi другой инвариант. На этом инварианте, как и на виртуальных конструкторах, довольно много построено. Другой язык, другие танцы - ну так вышло :)


> Если позволить менять порядок вызовов конструкторов (т.е.
>  сначала наследника, потом базового), то можно будет обращаться
> к методам подобъекта базового ДО того, как у него отработает
> конструктор.


Насколько мне память не изменяет, это связано еще и с множественным наследованием - то есть, на каком-то этапе неизвестно кого вызывать. В языках, где нет множественного наследования реализации, такой проблемы не существует.


> В Дельфи все классы пределах юнита френды по умолчанию,
> так что близкое понятие таки есть :)


Смешно.


 
Alkid ©   (2010-03-05 17:27) [89]


> Игорь Шевченко ©   (05.03.10 17:18) [88]
> В Delphi другой инвариант. На этом инварианте, как и на
> виртуальных конструкторах, довольно много построено. Другой
> язык, другие танцы - ну так вышло :)

Какой инвариант?


> Насколько мне память не изменяет, это связано еще и с множественным
> наследованием - то есть, на каком-то этапе неизвестно кого
> вызывать. В языках, где нет множественного наследования
> реализации, такой проблемы не существует.

Не совсем. Множественное наследование порождает свои проблемы, но среди них нет проблемы с порядком вызова конструкторов. Всегда действует простое правило: "сначала базовые, потом наследники". Это правило ортогонально способу наследования.


> Смешно.

Поясни. Разве я не прав, что классы в одном юните видят приватные/защищенные члены другого? Если да, то чем это принципиально отличается от концепции дружественных классов в С++?


 
Владислав ©   (2010-03-05 17:28) [90]

> Alkid ©   (05.03.10 17:08) [85]

По этому поводу уже написано здесь:

> Anatoly Podgoretsky ©   (05.03.10 15:05) [49]


 
Alkid ©   (2010-03-05 17:38) [91]


> Владислав ©   (05.03.10 17:28) [90]

Не аргумент :) Его можно под любое ограничение, заложенное в язык, подвести. Например, под инкапсуляцию данных. Зачем нам защищенные и приватные члены? Даешь полный доступ ко всему!


 
Игорь Шевченко ©   (2010-03-05 17:39) [92]

Alkid ©   (05.03.10 17:27) [89]


> Какой инвариант?


Ручное управление порядком вызова конструкторов, вызовом методов предков.


> Поясни. Разве я не прав, что классы в одном юните видят
> приватные/защищенные члены другого?


Видят. Без слова strict. В пределах одного модуля. Friend в C++ несколько иначе работает, не находишь ? Там правда и модулей нету :)


> Не совсем. Множественное наследование порождает свои проблемы,
>  но среди них нет проблемы с порядком вызова конструкторов.
>


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

Кстати, насчет порядка вызовов, вот такой пример (на Delphi):

type
 TFooForm = class(TForm)
 ....
    procedure OnCreate (Sender: TObject);
 private
   FBar: TFooBar;
 public
   constructor Create (AOwner: TComponent; ABar: TFooBar); reintroduce;
 end;

procedure TFooForm.OnCreate (Sender: TObject);
begin
 with FBar do
 begin
   Bazz;
   Boom;
   Bang;
 end;
....
end;

constructor TFooForm.Create (AOwner: TComponent; ABar: TFooBar);
begin
 FBar := ABar;
 inherited Create (AOwner);
end;


Так как OnCreate вызывается конструктором базового класса (при OldCreateOrder, начиная с какой-то версии Delphi), каким образом можно передать параметр классу для того, чтобы он имел смысл в обработчике OnCreate ?


 
GrayFace ©   (2010-03-05 18:14) [93]

Alkid ©   (05.03.10 17:16) [87]
Когда вообще допустимо пользоваться экземпляром ДО его конструирования?

Когда вызывается код конструктора объект уже инициализирован нулями и проставлены все указатели на таблицы виртуальных функций. Конструкторы выполняют только последующую инициализацию. Что недопустимого?

А инварианты не такие уж инвариантные:
class A
{
public:
 int val;
 A() { val = 5; }
 A(int v) { val = 5; }
 int ShowVal()
 {
   if(val == 5)
     MessageBoxA(0, "5", "", 0);
   else
     MessageBoxA(0, "not 5", "", 0);
   return 0;
 }
};

class B : public A
{
public:
B() : TA(ShowVal()) {}
};

В VC++ запускается и показывает "not 5".


 
GrayFace ©   (2010-03-05 18:16) [94]

B() : TA(ShowVal()) {}
имеется в виду
B() : A(ShowVal()) {}


 
Alkid ©   (2010-03-05 18:23) [95]


> Игорь Шевченко ©   (05.03.10 17:39) [92]
> Ручное управление порядком вызова конструкторов, вызовом
> методов предков.

Это не инвариант. Это как раз его отсутствие.


> Видят. Без слова strict. В пределах одного модуля. Friend
> в C++ несколько иначе работает, не находишь ? Там правда
> и модулей нету :)

Разница косметическая, а принцип один - есть возможность легально обходить защиту членов, создавая "полупрозрачные" области видимости.


> Я имел в виду не с порядком вызова конструкторов, а с возможностью
> вызова виртутальных методов в конструкторах.

Все правильно - возможность контролировать порядок вызовов конструкторов и возможность вызывать виртуальные методы в конструкторе - вещи комплементарные. Второе не имеет смысла без первого (вызовы всегда будут в не созданный объект, что небезопасно по определению. Разработчики сказали "А" и вполне обоснованно сказали "Б".

Но я критикую именно "А". Да, это фитча дельфи, но я считаю, что это фитча плохая.


> Так как OnCreate вызывается конструктором базового класса
> (при OldCreateOrder, начиная с какой-то версии Delphi),
> каким образом можно передать параметр классу для того, чтобы
> он имел смысл в обработчике OnCreate ?

Только поменяв порядок вызова конструкторов :)
Но надо понимать, что в таком случае по всей цепочке наследования до того класса, где вызывается OnCreate, конструкторы должны быть устроены так: "сначала свой код, потом вызов предка".

Но у меня есть вопрос - а зачем вызывать OnCreate из базового класса? Что мешает вызвать его в конструкторе TFooForm?


 
Игорь Шевченко ©   (2010-03-05 18:29) [96]

Alkid ©   (05.03.10 18:23) [95]


> Разница косметическая


Фигасе косметическая. friend объявленный в классе дает возможность доступа к своим потрохам из любого места, лишь бы домогающийся был правильного типа. А в Delphi доступайся кто хочет, но только в пределах модуля. Это хорошо, когда модуль есть, не правда ли ? А когда его нету - и варенья тоже нету.


> Да, это фитча дельфи, но я считаю, что это фитча плохая


А я не вижу ничего плохого. Свойство языка, какое есть, такое есть.


> Но у меня есть вопрос - а зачем вызывать OnCreate из базового
> класса? Что мешает вызвать его в конструкторе TFooForm?


Затем, что он оттуда вызывается. Никого не спрашивая - так устроен конструктор базового класса и поменять его - ну никакой возможности нету.


 
Alkid ©   (2010-03-05 18:31) [97]


> GrayFace ©   (05.03.10 18:14) [93]
> А инварианты не такие уж инвариантные:

В С++ тоже дыр хватает, кто же спорит-то? ;)


> Когда вызывается код конструктора объект уже инициализирован
> нулями и проставлены все указатели на таблицы виртуальных
> функций. Конструкторы выполняют только последующую инициализацию.
>  Что недопустимого?

Очень просто - не всегда можно и удобно написать класс, у которого инициализированные нулями поля представляет собой валидное состояние экземпляра. Представь, что одно из полей - ссылка на другой объект и при вызове метода класса вызывается метод по этой ссылке. Если в ссылке будет ноль, то ты получаешь AV/NRE.

Проверять каждый раз каждое поле на то, что оно было проинициализировано? Заставлять каждого потребителя твоего класса знать тонкости реализации? Смотри мой пример в [68], он являет собой похожий пример. В С++ там, конечно, в message будет мусор, а не ноль, но это не принципиально различие, будет AV не по нулевому адресу, а по случайному. Подобный пример можно написать для C# и там будет NRE.


 
GrayFace ©   (2010-03-05 18:38) [98]

Alkid ©   (05.03.10 18:31) [97]
Заставлять каждого потребителя твоего класса знать тонкости реализации?

Заставить их не вызывать функции класса до вызова его конструктора.


 
Alkid ©   (2010-03-05 18:50) [99]


> Игорь Шевченко ©   (05.03.10 18:29) [96]
> Фигасе косметическая. friend объявленный в классе дает возможность
> доступа к своим потрохам из любого места, лишь бы домогающийся
> был правильного типа. А в Delphi доступайся кто хочет, но
> только в пределах модуля. Это хорошо, когда модуль есть,
>  не правда ли ? А когда его нету - и варенья тоже нету.

Отсутствие модулей в С++ - это его большой недостаток, не спорю :)
Что касается friend/strict - то да, я таки считаю, что это разные реализации одной идеи со своими плюсами и минусами. Просто ты сформулировал, что в Дельфи нет "близкого понятия". Я не согласен.


> А я не вижу ничего плохого. Свойство языка, какое есть, такое есть.

Бывают удачные свойства, и неудачные. Это я отношу к неудачным.
Так же как, например, отсутствие модульности в С++.

Почему я считаю это свойство неудачным? Потому что автор класса должен иметь в виду возможность вызова метода у его класса до того, как будет вызван конструктор. Если автор этого не сделает, то он создает потенциально небезопасный класс. Если он делает, то ему придется нагружать код параноидальными проверками на то, что метод вызван после отработки конструктора. Можно, конечно, говорить о стеклянном члене, о том, что умные программисты не будут нарушать правил, но это действует только до тех пор, пока аудитория, использующая код, ограничена.


 
Alkid ©   (2010-03-05 18:52) [100]


> GrayFace ©   (05.03.10 18:38) [98]

А зачем тогда вообще давать программисту в руки такую возможность?


 
Игорь Шевченко ©   (2010-03-05 19:08) [101]

Alkid ©   (05.03.10 18:50) [99]


> Просто ты сформулировал, что в Дельфи нет "близкого понятия".
>  Я не согласен.


Ну так его же нету. Можно быть несогласным, от этого понятие не появится :)

В С++ доступ к телу дается для доверенных типов отовсюду. Ну нету там возможности выделить модули. Но только для доверенных типов. В Delphi наоборот, доступ открыт любому типу, но только внутри модуля и если не указано слово strict.


> автор класса должен иметь в виду возможность вызова метода
> у его класса до того, как будет вызван конструктор. Если
> автор этого не сделает, то он создает потенциально небезопасный
> класс. Если он делает, то ему придется нагружать код параноидальными
> проверками на то, что метод вызван после отработки конструктора


Ну вот VCL как-то обходится без паранойи. На самом деле конечно никто параноей не страдает, вызовут не вовремя - получат по морде (ну или иной побочный эффект), так что слухи о навороченных способах проверки вызовов до конструктора сильно преувеличены.


 
GrayFace ©   (2010-03-05 19:30) [102]

Alkid ©   (05.03.10 18:52) [100]
А зачем тогда вообще давать программисту в руки такую возможность?

Чтобы он сам решал, когда ей пользоваться. Конкретно эта возможность (существующая и в VC++) если и нужна, то очень редко.

А возможность вызова виртуальных функций нужна, например, для такого:
TA = class
public
 procedure Open(const FileName: string); virtual;
 constructor Create;
 constructor Create(const FileName: string); // вызывает Open
end;

TB = class
public
 procedure Open(const FileName: string); override;
end;

В C++ в такой ситуации, перекрывая Open (или любую функцию, которую вызывает Open!), надо имплементить в наследнике и Create(const FileName: string). Это вопиющая неочевидость, да еще мы должны точно знать, что делает Create(const FileName: string) базового класса и это должно оставаться неизменным.


 
Piter ©   (2010-03-05 20:16) [103]

по-моему, вы сейчас спорите, что зеленый цвет лучше красного. И приплетаете сюда дифракцию...


 
Kerk ©   (2010-03-05 20:38) [104]


> @!!ex ©   (05.03.10 15:48) [60]
>
> Delphi без VCL имеет мало ценности.
> Писать в разных стилях в пределах одного проекта - плохо.

1) В больших проектах собственный код и VCL пересекаются только краешком;
2) Подход к разработке, где в конструкторе выполняется вся необходимая инициализация, позволяет писать более читаемый код. Т.к. программист сразу видит какие данные нужны для инициализации и после вызова конструктора сразу имеет пригодный к работе объект;
3) Структура VCL обусловлена поддержкой визуальной настройки в design time, непонятно зачем копировать такой подход в классах для того не предназначенных.


 
Kerk ©   (2010-03-05 20:42) [105]

В частности, из-за того, что ты отстаиваешь, VCL не будет никогда полноценно поддерживать многопоточность.


 
Alkid ©   (2010-03-05 20:45) [106]


> Игорь Шевченко ©   (05.03.10 19:08) [101]
> Ну так его же нету. Можно быть несогласным, от этого понятие
> не появится :)

Давай сойдемся на том, что понятие "близость понятий" - субъективное и закончим эту часть спора? :)


> Ну вот VCL как-то обходится без паранойи. На самом деле
> конечно никто параноей не страдает, вызовут не вовремя -
>  получат по морде (ну или иной побочный эффект), так что
> слухи о навороченных способах проверки вызовов до конструктора
> сильно преувеличены.

Я знаю, что без паранойи. Что касается "по морде" - оно, конечно, да, но лучше, что бы этот класс ошибок был вообще исключен. Это как со статической типизацией. В принципе можно и без нее, она накладывает свои ограничения, но она дает определенные ништяки, позволяя устранять целые классы ошибок в программе.


> GrayFace ©   (05.03.10 19:30) [102]

Это опасная возможность. Так что я всячески выступаю против нее, тем более, что есть средства избежать необходимости так делать.


 
Alkid ©   (2010-03-05 20:47) [107]


> Piter ©   (05.03.10 20:16) [103]

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


 
vuk ©   (2010-03-05 21:03) [108]

Я понял. Правильный конструктор вообще ничего не должен делать. Во избежание.


 
Игорь Шевченко ©   (2010-03-05 21:08) [109]

Alkid ©   (05.03.10 20:45) [106]


> Давай сойдемся на том, что понятие "близость понятий" -
> субъективное и закончим эту часть спора? :)


Давай сойдемся и закончим.


>  Это как со статической типизацией. В принципе можно и без
> нее, она накладывает свои ограничения, но она дает определенные
> ништяки, позволяя устранять целые классы ошибок в программе.
>


С этим тоже не буду спорить. При огульном использовании типа variant, например, ошибки времени компиляции переносятся на время выполнения.

Kerk ©   (05.03.10 20:38) [104]


> 3) Структура VCL обусловлена поддержкой визуальной настройки
> в design time, непонятно зачем копировать такой подход в
> классах для того не предназначенных.


С этого момента подробнее. Даже будучи созданными с конструкторами без параметров классы VCL успешно инициализируются значениями по умолчанию. Еще существуют классы RTL, которым визуальные настройки вообще безразличны.


> 1) В больших проектах собственный код и VCL пересекаются
> только краешком;


С этого момента опять же, подробнее - код исполняемой системы любой среды, будь то .Net, Java и прочие VBRUN и не должен "пересекаться". С учетом его должен быть свой код спроектирован, только и всего.


 
Kerk ©   (2010-03-05 21:22) [110]


> Игорь Шевченко ©   (05.03.10 21:08) [109]
> С этого момента подробнее. Даже будучи созданными с конструкторами
> без параметров классы VCL успешно инициализируются значениями
> по умолчанию. Еще существуют классы RTL, которым визуальные
> настройки вообще безразличны.

Это в общем, другой вопрос. Значения по умолчанию просто позволяют чуть меньше работать руками, но от проблемы не избавляют.

Про остальной RTL хотелось бы конкретных примеров. Тут уже вспоминали TIniFile и TThread, которые принимают параметры инициализации в конструктор. TStringList, который вспоминался как контрпример, дополнительно инициализировать не нужно, создал и сразу работай.

> С этого момента опять же, подробнее - код исполняемой системы
> любой среды, будь то .Net, Java и прочие VBRUN и не должен
> "пересекаться". С учетом его должен быть свой код спроектирован,
>  только и всего.

Под пересечением я имел ввиду связность кода. Как правило какое-то взаимодействие с VCL осуществляет относительно небольшой слой кода программы.


 
Игорь Шевченко ©   (2010-03-05 21:30) [111]

Kerk ©   (05.03.10 21:22) [110]


> TStringList, который вспоминался как контрпример, дополнительно
> инициализировать не нужно, создал и сразу работай.


Да его и нечем особенно инициализовать.


> Про остальной RTL хотелось бы конкретных примеров


если примеры классов, имеющих конструкторы с параметрами, то в Db их много.


> Как правило какое-то взаимодействие с VCL осуществляет относительно
> небольшой слой кода программы


Это смотря какая программа. Всяческие обработчики событий, работа с наборами данных и т.п. таки подразумевает взаимодействие с VCL


 
Kerk ©   (2010-03-05 21:37) [112]


> Игорь Шевченко ©   (05.03.10 21:30) [111]
> если примеры классов, имеющих конструкторы с параметрами,
>  то в Db их много.

Ну дык я как раз и отстаиваю такой подход, что вся необходимая инициализация должна производится в конструкторе :)

> Это смотря какая программа. Всяческие обработчики событий,
>  работа с наборами данных и т.п. таки подразумевает взаимодействие
> с VCL

Тут я не буду спорить. Просто в общем случае нет причин полностью копировать подход к разработке из VCL (конструкторы без параметров). Тем более, что эти два подхода не конфликтуют.


 
Юрий Зотов ©   (2010-03-05 21:42) [113]

Но радует то, что уровень многих, давно посещающих этот форум сильно вырос. Во всяком случае, раньше они в подобных ветках участия не принимали.

Значит, не зря все же, а?
:o)


 
Kerk ©   (2010-03-05 21:47) [114]


> Юрий Зотов ©   (05.03.10 21:42) [113]

Огласите весь список, пожалуйста (с)
:)


 
Юрий Зотов ©   (2010-03-05 21:53) [115]


> Kerk ©   (05.03.10 21:47) [114]


К счастью, он довольно большой.
:o)

Да и нет нужды - каждый про себя и сам знает.
:o)


 
Игорь Шевченко ©   (2010-03-05 21:56) [116]

Kerk ©   (05.03.10 21:37) [112]


> Ну дык я как раз и отстаиваю такой подход, что вся необходимая
> инициализация должна производится в конструкторе :)


Так для инициализации не всегда нужны параметризованные конструкторы.


 
Kerk ©   (2010-03-05 21:57) [117]


> Игорь Шевченко ©   (05.03.10 21:56) [116]
> Так для инициализации не всегда нужны параметризованные
> конструкторы.

В случае TStringList, да. Потому там конструктор без параметров.
Или ты о другом?


 
Игорь Шевченко ©   (2010-03-05 22:11) [118]

Kerk ©   (05.03.10 21:57) [117]

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


 
Kerk ©   (2010-03-05 22:14) [119]


> Игорь Шевченко ©   (05.03.10 22:11) [118]

Ну значит не нужны им эти параметры.

Я напомню, с чего начался разговор:

> @!!ex ©   (05.03.10 10:00) [5]
>
> И вообще, конструктор не должен делать ничего такого, что
> может вызвать исключение.
> Для этого есть другие методы, например Init и LoadFromFile.
> ..
> По хорошему конструктор должен только инициализировать объект
> значениями по умолчению.
> А настраиватся объект должен отдельно.

Я с этим не согласен. Если инициализация есть, то она должна быть произведена в конструкторе. Если нет, то и проблемы нет :)


 
Игорь Шевченко ©   (2010-03-05 22:47) [120]

Kerk ©   (05.03.10 22:14) [119]


> > И вообще, конструктор не должен делать ничего такого,
> что
> > может вызвать исключение.
> > Для этого есть другие методы, например Init и LoadFromFile.
>


Ну это слишком фанатично. И не слишком умно. Как говорил один неглупый человек, задача должна быть решена минимальным числом максимально понятных строка кода. Делать намеренно два метода, один Create, другой Init, значит противоречить этому утверждению.


 
Piter ©   (2010-03-05 22:54) [121]

Kerk ©   (05.03.10 22:14) [119]
Я с этим не согласен


поддерживаю. Я не понимаю зачем нужно:

My := MyClass.Create;
My.Init;

Если можно так:

My := MyClass.Create;

Я могу понять, что кто-то к чему-то привык, это на самом деле важно, привычки позволяют нам не ломать голову на пустом месте. Но если говорить абстрактно, не касаясь опыта того или иного человека, то я не вижу разницы в этих двух подходах. Не позволяет / не приветствует средство разработки вызов исключения в конструкторе - вариант номер 1. Разрешает - вариант номер два.

Да и как уже отметили, код конструктора в дельфовом понимании это скорее похоже на событие, выполнение некоего участка кода с посылом "объект создан, давай инициализируй что тебе нужно". Можно рассматривать это как событие OnCreate. С некоторой магией:

1) вызов конструктора неявно приводит к вызову NewInstance
2) вызов исключения в конструкторе неявно приводит к вызову деструктора


 
Anatoly Podgoretsky ©   (2010-03-05 23:09) [122]

> vuk  (05.03.2010 21:03:48)  [108]

Правильно, пусть что то делает деструктор, он же не конструктор.


 
Kerk ©   (2010-03-05 23:30) [123]


> Piter ©   (05.03.10 22:54) [121]
> Но если говорить абстрактно, не касаясь опыта того или иного
> человека, то я не вижу разницы в этих двух подходах.

Разница есть. Разработчик работает с кодом не один, да и он сам не идеален. А ошибиться в
MyComp := TMyComp.Create(a,b,c,d);
намного сложнее, чем в
MyComp := TMyComp.Create;
MyComp.Init(a);
MyComp.bProp := b;
MyComp.cProp := c;
MyComp.dProp := d;


 
Eraser ©   (2010-03-05 23:33) [124]

> [121] Piter ©   (05.03.10 22:54)


> поддерживаю. Я не понимаю зачем нужно:
>
> My := MyClass.Create;
> My.Init;
>
> Если можно так:
>
> My := MyClass.Create;

вот так нельзя, если есть явная опасность исключения, например в TFileStream.Create (один из немногих примеров такого типа, который часто попадается в жизни).

в этом случае нужно городить лишний обработчик исключения.

а убери они открытие/создание файла в Init. Можно было бы использовать стандартный подход.

FS := TFileStream.Create();
try
 FS.Init(file, mode);
finally
 FS.Free;
end;


хорошим тоном было бы сделать и такой тип конструктора, на то и придумана overload.


 
Kerk ©   (2010-03-05 23:56) [125]


> Eraser ©   (05.03.10 23:33) [124]

А сделай они вместо исключения в Init возврат кода ошибки, то можно было бы обойтись вообще без try-finally. На то и придумана overload.

Шутка:)
Но как всегда с долей шутки.


 
Piter ©   (2010-03-05 23:59) [126]

Kerk ©   (05.03.10 23:30) [123]

тут я не согласен. Почему присвоение параметров в 4 строки более ошибочно, чем присвоение в одной строчке? Удобнее в одну строку - да (если все параметры обязательно надо задать), но менее ошибочно?

Eraser ©   (05.03.10 23:33) [124]
в этом случае нужно городить лишний обработчик исключения


почему лишний и почему городить? Сработает просто вышестоящий обработчик исключений.


 
Kerk ©   (2010-03-06 00:01) [127]


> Piter ©   (05.03.10 23:59) [126]
>
> Kerk ©   (05.03.10 23:30) [123]
>
> Почему присвоение параметров в 4 строки более ошибочно,
> чем присвоение в одной строчке?

Как минимум потому, что без всякой документации сам конструктор говорит тебе какие данные нужны для инициализации класса.


 
Демо ©   (2010-03-06 00:04) [128]


> тут я не согласен. Почему присвоение параметров в 4 строки
> более ошибочно, чем присвоение в одной строчке? Удобнее
> в одну строку - да (если все параметры обязательно надо
> задать), но менее ошибочно?


Потому что для объекта может быть необходима инициализация всех четырёх полей.
Разработчик же запросто при таком подходе может оставить непроинициализированным одно из них.


 
Eraser ©   (2010-03-06 00:11) [129]

> [126] Piter ©   (05.03.10 23:59)


> Сработает просто вышестоящий обработчик исключений.

а если его нету? т.к. при конструировании других объектов исключений быть не может. тогда для TFileStream нужно городить отдельно. ничего катастрофического в этом нет, но мешает.

насчет параметров в конструкторе - не вижу ничего плохого.


 
Игорь Шевченко ©   (2010-03-06 00:16) [130]


> т.к. при конструировании других объектов исключений быть
> не может


Может


 
Eraser ©   (2010-03-06 00:37) [131]

> [130] Игорь Шевченко ©   (06.03.10 00:16)

и много таких классов в VCL?


 
Игорь Шевченко ©   (2010-03-06 00:38) [132]

Eraser ©   (06.03.10 00:37) [131]

Все


 
vuk ©   (2010-03-06 00:43) [133]

to Anatoly Podgoretsky ©   (05.03.10 23:09) [122]

> Правильно, пусть что то делает деструктор, он же не конструктор.

А вдруг и там что-то случится? :) Недоразрушенные экземпляры ничем не лучше, чем недоинициализированные. Даже хуже.

P.S. Не, я предлагаю ваще нигде ничего не делать. Совсем ошибок не будет!


 
Eraser ©   (2010-03-06 00:55) [134]

> [132] Игорь Шевченко ©   (06.03.10 00:38)

ну если следовать такой логике, то почти в любой строчке кода можно на AV нарваться или еще что по хуже.
что то я не видел во всех классах внутри конструкторов raise ESomeException.


 
Игорь Шевченко ©   (2010-03-06 01:09) [135]

Eraser ©   (06.03.10 00:55) [134]


> ну если следовать такой логике, то почти в любой строчке
> кода можно на AV нарваться или еще что по хуже


http://transl-gunsmoker.blogspot.com/2009/03/blog-post_2362.html


 
Германн ©   (2010-03-06 01:29) [136]


> Eraser ©   (06.03.10 00:37) [131]
>
> > [130] Игорь Шевченко ©   (06.03.10 00:16)
>
> и много таких классов в VCL?


> Игорь Шевченко ©   (06.03.10 00:38) [132]
>
> Eraser ©   (06.03.10 00:37) [131]
>
> Все
>

Один про Фому, другой про Ерёму, имхо.


 
Германн ©   (2010-03-06 01:34) [137]


> Игорь Шевченко ©   (06.03.10 01:09) [135]
>
> Eraser ©   (06.03.10 00:55) [134]
>
>
> > ну если следовать такой логике, то почти в любой строчке
> > кода можно на AV нарваться или еще что по хуже
>
>
> http://transl-gunsmoker.blogspot.com/2009/03/blog-post_2362.
> html
>

Если бы я или кто другой привел примеры из этой статьи в доказательство своих утверждений о чем-то, ты Игорь был бы первым, кто сказал бы, что виноват я. Ибо не следует так писать компоненты.


 
Eraser ©   (2010-03-06 02:10) [138]

> [135] Игорь Шевченко ©   (06.03.10 01:09)

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


 
Германн ©   (2010-03-06 02:23) [139]


> Eraser ©   (06.03.10 02:10) [138]
>
> > [135] Игорь Шевченко ©   (06.03.10 01:09)
>
> не ясна мораль данного примера, в контексте дискуссии данной
> ветки.
>

Один про Фому, другой про Ерёму, имхо. :)

т.е. Один про генофонд, другой х.з. о чем :)


 
Игорь Шевченко ©   (2010-03-06 02:26) [140]

Eraser ©   (06.03.10 02:10) [138]

Мораль данного примера состоит в том, что код с исключениями надо писать с учетом того, что исключение может возникнуть практически в любой строке кода. В частности, даже при банальном вызове TObject.Create может возникнуть исключение EOutOfMemory


 
Аноним ©   (2010-03-06 02:30) [141]


> В частности, даже при банальном вызове TObject.Create может
> возникнуть исключение EOutOfMemory

что делать в такой ситуации? :-)


 
Eraser ©   (2010-03-06 02:35) [142]

> [140] Игорь Шевченко ©   (06.03.10 02:26)

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

в случае же TFileStream.Create исключение - это вполне штатное поведение.


 
Аноним ©   (2010-03-06 02:38) [143]


> для таких вещей, по-моему, больше подходит централизованная
> обработка исключений

лисп, схема, хаскелл :-)


 
Германн ©   (2010-03-06 02:41) [144]


> Игорь Шевченко ©   (06.03.10 02:26) [140]
>
> Eraser ©   (06.03.10 02:10) [138]
>
> Мораль данного примера состоит в том, что код с исключениями
> надо писать с учетом того, что исключение может возникнуть
> практически в любой строке кода. В частности, даже при банальном
> вызове TObject.Create может возникнуть исключение EOutOfMemory
>

Паранойя. (Но только LVT относится к ней с юмором :)
Имхо.
Плюс "перебор". Тоже имхо.


 
Игорь Шевченко ©   (2010-03-06 02:41) [145]

Eraser ©   (06.03.10 02:35) [142]


> если в программе где-то портится память, то там уже никакая
> обработка исключений не спасет


Она не портится, ее просто не хватает.
В ряде случаев обработка вполне может спасти.


> в случае же TFileStream.Create исключение - это вполне штатное
> поведение


Что есть "штатное":  Не смог открыться файл в режиме fmOpen (прав не хватает) - это штатное исключение ?
Не смог создаться файл в режиме fmCreate (нет места на диске) - это штатное и ожидаемое исключение ?
Чем отличается от нехватки памяти ?

или наличие слова raise в конструкторе смущает незрелые умы и они готовы делать стойку: Ага, Борланд нарушил пятнадцатую суру Корана!

Так в ряде случаяв наличие raise лучше, чем его отсутствие.


 
Аноним ©   (2010-03-06 02:43) [146]


> Германн ©   (06.03.10 02:41) [144]

шизофазия?


 
Игорь Шевченко ©   (2010-03-06 02:53) [147]

Кстати, о параметрах, цитата:

"Минимизируйте число параметров, передаваемых в метод, или, что еще важнее, передавайте только те параметры, которые нужны для поддержания абстракции, формируемой интерфейсом метода"

Стив МакКоннел, "Совершенный код".

То есть, в конструктор имеет смысл передавать те параметры, без которых создаваемый объект не имеет смысла.


 
Германн ©   (2010-03-06 03:15) [148]


> Игорь Шевченко ©   (06.03.10 02:53) [147]
>
> Кстати, о параметрах, цитата:
>
> "Минимизируйте число параметров, передаваемых в метод, или,
>  что еще важнее, передавайте только те параметры, которые
> нужны для поддержания абстракции, формируемой интерфейсом
> метода"
>
> Стив МакКоннел, "Совершенный код".
>
> То есть, в конструктор имеет смысл передавать те параметры,
>  без которых создаваемый объект не имеет смысла.
>


> не ясна мораль данного примера, в контексте дискуссии данной
> ветки.


 
Германн ©   (2010-03-06 03:17) [149]

Похоже кто-то стремится попасть в мой черный список.
Я сей список уже почти забыл, но только почти. :)


 
Eraser ©   (2010-03-06 04:13) [150]

> [145] Игорь Шевченко ©   (06.03.10 02:41)


> Что есть "штатное":

это значит, что ожидаемое.
то есть при открытии файла мы знаем, что в TFileStream.Create может произойти исключение. соотвественно, можно заранее это предусмотреть, добавив обработчик исключения.

> Чем отличается от нехватки памяти ?

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


 
@!!ex ©   (2010-03-06 07:02) [151]

> [147] Игорь Шевченко ©   (06.03.10 02:53)

Эм. Ну собственно и я о том же.


 
GrayFace ©   (2010-03-06 07:46) [152]

Alkid ©   (05.03.10 20:45) [106]
Это опасная возможность. Так что я всячески выступаю против нее, тем более, что есть средства избежать необходимости так делать.


Какие средства? По-моему, только одно - делать в конструкторах лишь базовую инциализацию.
(под перегрузкой в примере я имел в виду override)
1) Вызов виртуальных методов. В чем опасность?
Если бездумно написать вызов виртуального метода - в C++ будет [102], в Delphi может получиться [68]. Если писать обдуманно, то опасности не будет. В Delphi перед вызовом F будет выполнен виртуальный конструктор/инициализватор, который будет override"нут в предке, чтобы инициализировать message. Т.е. подходы C++ и Delphi в данном аспекте я считаю одинаково опасными/безопасными.
2) Вызов методов предка до вызова его конструктора. Опасность очевидна, лучше так не делать без явной необходимости.

Eraser ©   (05.03.10 23:33) [124]
вот так нельзя, если есть явная опасность исключения, например в TFileStream.Create (один из немногих примеров такого типа, который часто попадается в жизни).

в этом случае нужно городить лишний обработчик исключения.

а убери они открытие/создание файла в Init. Можно было бы использовать стандартный подход.

FS := TFileStream.Create();
try
FS.Init(file, mode);
finally
FS.Free;
end;

хорошим тоном было бы сделать и такой тип конструктора, на то и придумана overload.


Че-то я не понимаю. Может речь о наоборот? Что вместо FS := TFileStream.Create(file, mode) в случае Init пришлось бы городить обработчик исключений?


 
Вася   (2010-03-06 09:25) [153]


> Че-то я не понимаю. Может речь о наоборот? Что вместо FS
> := TFileStream.Create(file, mode) в случае Init пришлось
> бы городить обработчик исключений?
>


Вроде как два примера абсолютно равнозначны, вариант с Init просто длиннее на один вызов (этого самого Init), целесообразность мягко говоря сомнительна


 
@!!ex ©   (2010-03-06 09:48) [154]

> [153] Вася   (06.03.10 09:25)

У этого самого Init легко делается обработчик исключений.
В то время как для обработки исключения Create как раз и придется городить огород


 
@!!ex ©   (2010-03-06 09:50) [155]

> Че-то я не понимаю. Может речь о наоборот? Что вместо FS
> := TFileStream.Create(file, mode) в случае Init пришлось
> бы городить обработчик исключений?

Покажи как грамотно обработать исключение вызванное в Create


 
Kerk ©   (2010-03-06 10:31) [156]


> Игорь Шевченко ©   (06.03.10 02:53) [147]
>
> Кстати, о параметрах, цитата:
>
> "Минимизируйте число параметров, передаваемых в метод, или,
>  что еще важнее, передавайте только те параметры, которые
> нужны для поддержания абстракции, формируемой интерфейсом
> метода"
>
> Стив МакКоннел, "Совершенный код".
>
> То есть, в конструктор имеет смысл передавать те параметры,
>  без которых создаваемый объект не имеет смысла.

Ну дык ежу понятно, что если для инициализации класса нужно 3 параметра, то совсем не обязательно в конструктор передавать их 10.


 
KSergey ©   (2010-03-06 10:38) [157]

> Игорь Шевченко ©   (05.03.10 15:58) [62]
> > Речь про штатное поведение дельфи.
> Штатным поведением Delphi запрещено перекрывать NewInstance ?

Не запрещается. Но делающий это человек должен четко понимать что в перекрытом методе он должен сделать.
Либо написать красными крупными буквами: "я подложил вам всем свинью, поэтому удобства и приятные плюшки дельфи типа Free - не работают".
Если он и этого не напишет - то это уже будет квалифицироваться как говнецо.


 
Piter ©   (2010-03-06 11:58) [158]

@!!ex ©   (06.03.10 9:50) [155]
Покажи как грамотно обработать исключение вызванное в Create


а чем оно отличается от обработки исключения вызванного в Init?

Если ты предполагаешь что это за исключение - так по его типу обработаешь его, независимо от того, в Create или Init оно произошло. Если неожиданное исключение - оно будет отработано вышестоящим общим обработчиком исключений.


 
DVM ©   (2010-03-06 13:05) [159]


> @!!ex ©   (06.03.10 09:48) [154]
> > [153] Вася   (06.03.10 09:25)
>
> У этого самого Init легко делается обработчик исключений.
>
> В то время как для обработки исключения Create как раз и
> придется городить огород
>
>

Нет никакой разницы


 
Вася   (2010-03-06 13:05) [160]


> @!!ex ©   (06.03.10 09:48) [154]
>
> > [153] Вася   (06.03.10 09:25)
>
> У этого самого Init легко делается обработчик исключений.
>
> В то время как для обработки исключения Create как раз и
> придется городить огород


не придется ничего городить.
в 99 процентах случаев такое исключение просто пропускается дальше, и ответственность за него возлагается на вызывающую сторону


> KSergey ©   (06.03.10 10:38)


> "я подложил вам всем свинью, поэтому удобства и приятные
> плюшки дельфи типа Free - не работают".


каким образом перекрытие NewInstance  может привести к неработе именно Free как плюшки? Не очень ясно


 
@!!ex ©   (2010-03-06 13:41) [161]

> [159] DVM ©   (06.03.10 13:05)
> Нет никакой разницы

Ок. Покажите пример. ;)


 
Kerk ©   (2010-03-06 13:56) [162]


> @!!ex ©   (06.03.10 13:41) [161]
>
> Ок. Покажите пример. ;)

Показать пример вышестоящего обработчика?


 
@!!ex ©   (2010-03-06 14:24) [163]

> [162] Kerk ©   (06.03.10 13:56)

ага. пример адекватно обрабатывающий исключение в конструкторе.


 
Kerk ©   (2010-03-06 14:31) [164]


> @!!ex ©   (06.03.10 14:24) [163]
>
> > [162] Kerk ©   (06.03.10 13:56)
>
> ага. пример адекватно обрабатывающий исключение в конструкторе.


function LoadData(...): Boolean;
var
 s: string;
begin
 Result := True;
 // какой-нибудь код
 try
   s := LoadFromFile(FileName);
 except
   Result := False;
   // ну и что еще надо
 end;
 // еще код
end;

function LoadFromFile(aFileName: string): string;
var
 FileStream: TFileStream;
begin
 FileStream := TFileStream.Create(aFileName);
 try
   // Тут всякое чтение и обработка
   // Result := ...
 finally
   FileStream.Free;
 end;
end;


Это если на практике. А вот если представлять сферический код в вакууме, то проблема, да.


 
@!!ex ©   (2010-03-06 14:39) [165]

FileStream := TFileStream.Create(aFileName);
try
  // Тут всякое чтение и обработка
  // Result := ...

  Ты в курсе что этот код выполнится при не валидно FileStream?


 
@!!ex ©   (2010-03-06 14:40) [166]

Я не правильно выразился.
Не выполнится, конечно же.


 
@!!ex ©   (2010-03-06 14:42) [167]

Только в примере отсутствует обработка исключения в конструкторе.
Обработка исключений только для:
  // Тут всякое чтение и обработка
  // Result := ...


 
Kerk ©   (2010-03-06 14:43) [168]


> @!!ex ©   (06.03.10 14:39) [165]
>
> FileStream := TFileStream.Create(aFileName);
> try
>   // Тут всякое чтение и обработка
>   // Result := ...
>   Ты в курсе что этот код не выполнится при не валидно FileStream?

Конечно не выполнится. Именно поэтому LoadData увидит проблему и вернет Falseю


 
Игорь Шевченко ©   (2010-03-06 14:50) [169]

@!!ex ©   (06.03.10 14:42) [167]


> Только в примере отсутствует обработка исключения в конструкторе.


Почему отсутствует ?


>  try
>    s := LoadFromFile(FileName);
>  except


вот она


 
@!!ex ©   (2010-03-06 15:11) [170]

> [169] Игорь Шевченко ©   (06.03.10 14:50)

Это на уровень выше.
С таким же успехом можно вообще не делать обработчик и сказать что исключение обработается стандартным обработчиком.


 
Игорь Шевченко ©   (2010-03-06 15:22) [171]

@!!ex ©   (06.03.10 15:11) [170]


> Это на уровень выше.


Не вижу принципиальной разницы между обработкой ошибок метода LoadFromFile и конкретной обработкой ошибки создания TFileStream.

На мой взгляд, исключение в процедуре сигнализирует о том, что загрузка не сложилась, а почему конкретно она не ложилась - это по типу исключения можно различить.


> С таким же успехом можно вообще не делать обработчик и сказать
> что исключение обработается стандартным обработчиком.


Можно. Только неграмотно.


 
Eraser ©   (2010-03-06 15:23) [172]

> [152] GrayFace ©   (06.03.10 07:46)


> в случае Init пришлось бы городить обработчик исключений?

в случае с Init обработчик уже должен быть создан, чаще всего анализ исключения и не требуется. требуется просто выход из метода или функции.
например.

FS := TFileStream.Create();
AnotherObject := TAnotherObject.Create();
Object3 := TObject3.Create();
try
 FS.Init(file, mode);
 ...
 Result := True;
finally
 FS.Free;
 AnotherObject.Free;
 Object3.Free;
end;
end;


 
GrayFace ©   (2010-03-06 15:23) [173]

@!!ex ©   (06.03.10 9:50) [155]
Покажи как грамотно обработать исключение вызванное в Create

Ты про это?
FS:= nil;
try
 FS:= TFilesStream.Create(file, mode);
except
 // обрабатываем
end;


 
Eraser ©   (2010-03-06 15:24) [174]

> FS := TFileStream.Create();
> AnotherObject := TAnotherObject.Create();
> Object3 := TObject3.Create();
> try
> FS.Init(file, mode);
> ...
> Result := True;
> finally
> FS.Free;
> AnotherObject.Free;
> Object3.Free;
> end;
> end;


function SomeFunction: Boolean;
begin
Result := False;
FS := TFileStream.Create();
AnotherObject := TAnotherObject.Create();
Object3 := TObject3.Create();
try
FS.Init(file, mode);
...
Result := True;
finally
FS.Free;
AnotherObject.Free;
Object3.Free;
end;
end;


 
GrayFace ©   (2010-03-06 15:26) [175]

Eraser ©   (06.03.10 15:23) [172]
FS := nil;
AnotherObject := TAnotherObject.Create();
Object3 := TObject3.Create();
try
 FS := TFileStream.Create(file, mode);
 ...
 Result := True;
finally
 FS.Free;
 AnotherObject.Free;
 Object3.Free;
end;
end;

Я бы так же создавал внутри try и Object3.


 
Eraser ©   (2010-03-06 15:31) [176]

> [175] GrayFace ©   (06.03.10 15:26)


> Я бы так же создавал внутри try и Object3.

Но, есть одно НО, тогда уже надо


FS := nil;
AnotherObject := TAnotherObject.Create();
Object3 := TObject3.Create();
try
FS := TFileStream.Create(file, mode);
try
  ...
  Result := True;
finally
  FS.Free;
end;
finally
FS.Free;
AnotherObject.Free;
Object3.Free;
end;
end;

;-)


 
@!!ex ©   (2010-03-06 15:44) [177]

> [173] GrayFace ©   (06.03.10 15:23)

Ага. А теперь мы в функции создает 10 объектов, которые могут сгенерировать исключение в конструкторе. Как будет? Достаточно на 2 написать, суть будет понятна.
Суть в том, что придется каждый Create в свой блок try finally оборачивать, чтобы корректно удалить объекты созданные ранее.
В случае с Init/LoadFromFile/etc будет только один блок try finally и при этом все будет корректно работать.


 
Дмитрий С ©   (2010-03-06 16:04) [178]


> В случае с Init/LoadFromFile/etc будет только один блок
> try finally и при этом все будет корректно работать.

Ну а как же ошибка OutOfMemory ?

И что плохого в обертке каждого Create в try finally ?


 
@!!ex ©   (2010-03-06 16:15) [179]

> [178] Дмитрий С ©   (06.03.10 16:04)

Это возникает, когда все сильно плохо.
Тут уже и свалить приложение не грех в большинстве случаев.


 
Eraser ©   (2010-03-06 16:26) [180]

> [178] Дмитрий С ©   (06.03.10 16:04)


> Ну а как же ошибка OutOfMemory ?

ты оборачиваешь каждое обращение к менеджеру памяти в try..except?

> И что плохого в обертке каждого Create в try finally ?

и, соответственно, каждый Create тоже?


 
Дмитрий С ©   (2010-03-06 16:48) [181]


> Eraser ©   (06.03.10 16:26) [180]

Стремлюсь к этому.
Кроме неявных случаев, где должен сработать сборщик мусора. Например:
SetLength(Str, 123);


 
@!!ex ©   (2010-03-06 16:56) [182]

> [181] Дмитрий С ©   (06.03.10 16:48)

Да ну. Никто так не делает.
Даже VCL пренебрегает такими ошибками.
Слишком мала вероятность их появления и слишком сложно реализовать штатное продолжение работы программы в таких ситуациях. А код поганится сильно.


 
GrayFace ©   (2010-03-06 17:17) [183]

Eraser ©   (06.03.10 15:31) [176]
Но, есть одно НО, тогда уже надо

Не надо. FS.Free происходит в общем try.

@!!ex ©   (06.03.10 15:44) [177]
Суть в том, что придется каждый Create в свой блок try finally оборачивать, чтобы корректно удалить объекты созданные ранее.

Не надо, см. [176].


 
Игорь Шевченко ©   (2010-03-06 17:25) [184]

Про исключения есть хорошая статья, там все собрано в одном месте:
http://www.delphikingdom.ru/asp/viewitem.asp?catalogid=1392


 
Дмитрий С ©   (2010-03-06 17:32) [185]


> Да ну. Никто так не делает.
> Даже VCL пренебрегает такими ошибками.
> Слишком мала вероятность их появления и слишком сложно реализовать
> штатное продолжение работы программы в таких ситуациях.
> А код поганится сильно.

Если не делать методы по 9000 строк, то не сильно и поганится.


> Даже VCL пренебрегает такими ошибками.

Очень грустно, если так...


 
@!!ex ©   (2010-03-06 17:55) [186]

> [183] GrayFace ©   (06.03.10 17:17)

FS1:=TFileStream.Create();
FS2:=TFileStream.Create(); <-- Тут исключение, FS1 не удален.


 
GrayFace ©   (2010-03-06 18:29) [187]

@!!ex ©   (06.03.10 17:55) [186]
FS2:= nil;
FS3:= nil;
FS4:= nil;
FS5:= nil;
FS1:= TFileStream.Create();
try
 FS2:= TFileStream.Create();
 FS3:= TFileStream.Create();
 FS4:= TFileStream.Create();
 FS5:= TFileStream.Create();
finally
 FS1.Free;
 FS2.Free;
 FS3.Free;
 FS4.Free;
 FS5.Free;
end;


 
oxffff ©   (2010-03-06 19:00) [188]


> GrayFace ©   (05.03.10 17:05) [84]
> oxffff ©   (05.03.10 14:57) [48]
> Берем перекрываем аллокатор newinstance без очистки на 0
> для неуправляемых объектов. Получаем исключение на free.
>  Гарантий нет!!!
> Это проблема того, кто перекрыл. Если кто-то хочет вызвать
> ошибку, то он найдет миллион способов это сделать.


Если следовать вашей логике, то и инициализация локальных переменных должна проходить по такой же схеме. Однако так не происходит.
Не объясните почему?


 
GrayFace ©   (2010-03-06 19:05) [189]

oxffff ©   (06.03.10 19:00) [188]
Не понял, что с инициализацией локальных переменных?


 
@!!ex ©   (2010-03-06 19:05) [190]

> [187] GrayFace ©   (06.03.10 18:29)

Красиво. :)
Только, что-то никто так не делает.
Мне вариант с LoadFromFile нравится больше.


 
Игорь Шевченко ©   (2010-03-06 19:05) [191]

GrayFace ©   (06.03.10 18:29) [187]


> FS2:= nil;
> FS3:= nil;
> FS4:= nil;
> FS5:= nil;


Вот это зачем ?


 
GrayFace ©   (2010-03-06 19:12) [192]

А, не инициализируются 0, если не string и т.п.?
1) Так сделано.
2) С локальными переменными чаще связана критическая по скорости функциональность и на ней это может сильнее сказаться, чем инициализация класса на скорости его создания.
3) Логика конструкторов/деструкторов сложнее, чем логика простой процедуры, для них это важнее.


 
GrayFace ©   (2010-03-06 19:17) [193]

Прошлый пост - ответ на [188].

Игорь Шевченко ©   (06.03.10 19:05) [191]
Вот это зачем ?

Иначе, если исключение вызовет FS2.Create, то FS2-5 останутся не инициализированными и Free вызовет AV.


 
Игорь Шевченко ©   (2010-03-06 19:23) [194]

GrayFace ©   (06.03.10 19:17) [193]


> Иначе, если исключение вызовет FS2.Create, то FS2-5 останутся
> не инициализированными и Free вызовет AV.


Чудны дела твои, господи.


 
oxffff ©   (2010-03-06 19:28) [195]


> GrayFace ©   (06.03.10 19:12) [192]
> А, не инициализируются 0, если не string и т.п.?
> 1) Так сделано.
> 2) С локальными переменными чаще связана критическая по
> скорости функциональность и на ней это может сильнее сказаться,
>  чем инициализация класса на скорости его создания.
> 3) Логика конструкторов/деструкторов сложнее, чем логика
> простой процедуры, для них это важнее.


1. Да.
2. Спорно. Бывает наоборот.
3. Уточните. Как связан Free c логикой конструкторов и деструкторов.


 
oxffff ©   (2010-03-06 19:40) [196]


> 2. Спорно. Бывает наоборот.


В смысле порой инициализация локальных переменных бывает медленней чем обнуление непрерывного участка.


 
GrayFace ©   (2010-03-06 20:33) [197]

oxffff ©   (06.03.10 19:28) [195]
3. Уточните. Как связан Free c логикой конструкторов и деструкторов.

Пожалуй, я поторопился. Это навеяно дискуссией с Alkid"ом.
Субьективно, инициализация нулями класса чаще полезна. И для ссылок на создаваемые в конструкторе объекты, и для event"ов, и для инициализируемых нулями полей. По сути, инициализация нулями оказывается лишней только для простых свойств, которым присваивается отличное от нуля значение в конструкторе - таких обычно не много.

Ну и еще пункт:
4) Не инициализированная локальная переменная приводит к warning"у, благодаря которому переодически отлавливается ошибка. При иициализации нулями ушла бы и эта проверка. В случае с классами такой проверки нет и иициализации нулями, наоборот, уменьшает вероятность ошибок, связанных с не инициализированными полями.


 
GrayFace ©   (2010-03-06 20:37) [198]

Причем, если та ситуация с Free, с которой мы начали, произойдет в процедуре, то выскочит этот warning.


 
Игорь Шевченко ©   (2010-03-06 20:41) [199]

GrayFace ©   (06.03.10 20:33) [197]


> В случае с классами такой проверки нет


И быть не может


 
Kerk ©   (2010-03-06 21:26) [200]

Необходимость делать вложенные try-finally скорее всего говорит только о том, что код нужно переписать (например, как в [164]). Это и на читаемости кода здорово скажется и утечек памяти мы не допустим, поленившись лишний раз try-finally написать.


 
DVM ©   (2010-03-07 00:43) [201]


> http://www.delphikingdom.ru/asp/viewitem.asp?catalogid=1392

Неплохая статья. Объем поражает.


 
Германн ©   (2010-03-07 00:45) [202]


> DVM ©   (07.03.10 00:43) [201]
>
>
> > http://www.delphikingdom.ru/asp/viewitem.asp?catalogid=1392
>
> Неплохая статья.

Дык автор работает (или работал) в суппорте Эврики.


 
Eraser ©   (2010-03-07 01:56) [203]

> [184] Игорь Шевченко ©   (06.03.10 17:25)

внушает. тянет на небольшую книгу )


 
31512 ©   (2010-03-07 07:55) [204]

По сабжу очень хорошо написано в http://www.delphikingdom.ru/asp/viewitem.asp?catalogid=346


 
GrayFace ©   (2010-03-07 10:33) [205]

Игорь Шевченко ©   (06.03.10 17:25) [184]
Про исключения есть хорошая статья, там все собрано в одном месте:
http://www.delphikingdom.ru/asp/viewitem.asp?catalogid=1392

Спасибо! Особенно полезно про дебаггер.


 
@!!ex ©   (2010-03-07 10:51) [206]

> [184] Игорь Шевченко ©   (06.03.10 17:25)

Спасибо. Очень интересно.


 
KSergey ©   (2010-03-07 19:09) [207]

> Игорь Шевченко ©   (06.03.10 19:23) [194]
> > Иначе, если исключение вызовет FS2.Create, то FS2-5 останутся
> > не инициализированными и Free вызовет AV.
> Чудны дела твои, господи.

А что не так? В чем чудность?
есть лишь один нюанс - не уточнено что за переменная FS. Если локальная на стеке - присвоение nil необходимо (при указанном в [187] построении кода), если член класса - то за нас это сделает дельфи, за что ей спасибо. Вот и все.


 
Игорь Шевченко ©   (2010-03-07 19:22) [208]

KSergey ©   (07.03.10 19:09) [207]


> А что не так? В чем чудность?


Неэстетично, доктор

А если код вызывает эстетическое неприятие, значит он, скорее всего, кривой.


 
Дмитрий С ©   (2010-03-11 08:44) [209]

При большом количестве строк в методе не всегда сразу ясно к какому try относится тот или иной finally, так же с begin и end.
Это реально затрудняет восприятие.
Жаль ide (2010) не может вертикальные полосочки рисовать для блоков.



Страницы: 1 2 3 4 5 6 вся ветка

Форум: "Прочее";
Текущий архив: 2010.08.27;
Скачать: [xml.tar.bz2];

Наверх





Память: 1.12 MB
Время: 0.066 c
15-1267047005
Юрий
2010-02-25 00:30
2010.08.27
С днем рождения ! 25 февраля 2010 четверг


15-1259160570
Игорь Шевченко
2009-11-25 17:49
2010.08.27
Желающие поговорить о лженауке - милости прошу


2-1274878317
Delphist2
2010-05-26 16:51
2010.08.27
копирование из spinedit


11-1216289923
BMouradov
2008-07-17 14:18
2010.08.27
потеря фокуса формы


6-1217938741
leonidus
2008-08-05 16:19
2010.08.27
Извлечение текста из WebBrowser`а





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский