Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2004.06.06;
Скачать: CL | DM;

Вниз

Насколько безопасен SetLength   Найти похожие ветки 

 
TUser ©   (2004-05-27 16:00) [0]

Насколько безопасен SetLength при работе с дин. массивами? Всегда ли он корректно выделяем/высвобождает память? Раньше никаких нареканий у меня не было, но сейчас ситуация такая. Есть код, который нормально работает. Теперь надо его запустить много раз. При этом вызовы соотв. функций осуществляются последовательно. Довольно часто вылетает AV и вываливается CPU window. Подозреваю, что это связано с тем, что какая-то память кушается и не освобождается в предыдущей итерации (хотя не уверен, что причина точно в этом). Первая итерация всегда происходит нормально. При трассировке удается дойти до различных участков кода, чаще всего остановка происходит именно на SetLength. Хотя и не обязательно, может произойти ошибка, например, на таком участке кода
if FileExists(FileName) then
  try
  sl:=TStringList.Create;
  sl.LoadFromFile(FileName) // <-- здесь
  finally
  sl.Free
  end;
Первая итерация всегда происходит нормально, на одной из дальнейших происходит ошибка. При этом при одинаковых исходных данных для некой итерации ошибка может произойти, а может и не произойти. Короче, совершенно не понимаю, где могут быть грабли. Буду рад услышать ценные советы по этому поводу, хотя понимаю, что тут скорее всего надо копать конкретную программу, а не слушать подобные рассуждения.


 
Digitman ©   (2004-05-27 16:04) [1]

непонятно, где здесь "итерация"
этот термин тесно связан с термином/концепцией "цикл"
цикл же в приведенном примере вряд ли кто наблюдает


 
Тимохов ©   (2004-05-27 16:09) [2]

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

Один из вопросов:

что плохого в таком коде?


> try
>   sl:=TStringList.Create;
>   sl.LoadFromFile(FileName) // <-- здесь
> finally
>   sl.Free
> end;


 
Sandman25+1   (2004-05-27 16:12) [3]

[2] Тимохов ©   (27.05.04 16:09)
Если вызывается 1 раз и sl - модульная/член класс, то еще ничего.
Бывало и хуже.

try
Screen.Cursor := crHourGlass;
...
except
 on E:Exception do Application.ShowException(E);
end;
Screen.Cursor := crDefault;

Такое ощущение, что человек не был знаком с try/finally.


 
Ega23 ©   (2004-05-27 16:12) [4]

> try
>   sl:=TStringList.Create;
>   sl.LoadFromFile(FileName) // <-- здесь
> finally
>   sl.Free
> end;

ИМХО,

try
sl:=TStringList.Create;
try
  sl.LoadFromFile(FileName) // <-- здесь
except
 ShowMessage
end;
finally
sl.Free;
end;

Это имелось ввиду?


 
Тимохов ©   (2004-05-27 16:14) [5]


> Ega23 ©   (27.05.04 16:12) [4]
> > try

нет - не это.

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


 
WebErr ©   (2004-05-27 16:27) [6]


> Тимохов ©   (27.05.04 16:14) [5]

Клёво, а вот я не угадал, где грабли, а где ещё пару таких тестов достать можно? 8)


 
Ega23 ©   (2004-05-27 16:33) [7]

В общем случае:

procedure TObject.Free;
asm
       TEST    EAX,EAX
       JE      @@exit
       MOV     ECX,[EAX]
       MOV     DL,1
       CALL    dword ptr [ECX].vmtDestroy
@@exit:
end;

Может я что-то не так понимаю, но разве не проверяется в
       TEST    EAX,EAX
экземпляр класса на nil?


 
Тимохов ©   (2004-05-27 16:38) [8]


> Может я что-то не так понимаю, но разве не проверяется в
>
>        TEST    EAX,EAX
> экземпляр класса на nil?

кто-нибудь говорил про класс?


 
Ega23 ©   (2004-05-27 16:41) [9]

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

Если это не про класс, то про что?


 
Тимохов ©   (2004-05-27 16:42) [10]


> Ega23 ©   (27.05.04 16:41) [9]

sl может быть локальной переменной процедуры, причем не проинициализированной.

Думаете не будет av?


 
Ega23 ©   (2004-05-27 17:12) [11]

sl может быть локальной переменной процедуры, причем не проинициализированной.

Думаете не будет av?

1. Я бы попросил ко мне на "ты". К вам на "вы" могу, если вам так проще будет.
2. Возможно мы друг-друга не понимаем, или говорим о разных вещах.
Насколько понял я:

где-то (возможно внутри данной функции) есть
var
sl:TStringList;

далее, в какой-то функции идёт вызов:

try
try
 sl:TStringList.Create;
 sl:TStringList.LoadFromFile(FileName);
except on E:Exception do
 ShowMessage(E.Message);
end;
finally
 sl.Free;
end;

Речь шла о том, что exception может возникнуть в момент отработки конструктора. Тогда (я могу ошибаться, но насколько мне известно) память, начавшая выделяться под данный экземпляр класса освобождается, самому sl приравнивают nil.
Дальше мы видим наш ShowMessage с текстом об ошибке.
Дальше проваливаемся в секцию finally sl.Free.
Но, насколько мне известно, Free отработает как раз от TObject? Проверит на nil, если <>nil, то вызовет деструктор.

Или я не прав?


 
Тимохов ©   (2004-05-27 17:17) [12]


> самому sl приравнивают nil.

в этом вы ошибаетесь.


 
Ega23 ©   (2004-05-27 17:20) [13]

> самому sl приравнивают nil.   в этом вы ошибаетесь.

Точно? Иначе мне очень уважаемым мною людям придётся претензии ставить.


 
Anatoly Podgoretsky ©   (2004-05-27 17:26) [14]

А где  Насколько безопасен SetLength

 sl:=TStringList.Create;
 try

должен быть здесь, иначе при ошибке будет еще одна ошибка.


 
Игорь Шевченко ©   (2004-05-27 17:28) [15]


> Точно? Иначе мне очень уважаемым мною людям придётся претензии
> ставить.


Точно. Откуда конструктор может знать, какой переменной присваивается его результат ?


 
Ega23 ©   (2004-05-27 17:28) [16]

АП нас рассудит!  :о)
Анатолий, объясните мне, убогому: получается, что от ошибки в конструкторе невозможно закрыться? Только выход из процедуры (функции) память освободит?


 
Anatoly Podgoretsky ©   (2004-05-27 17:30) [17]

Тимохов ©   (27.05.04 16:42) [10]
Может быть и глобальной переменной, полем класса, но имееющим в данный момент не нулевое значение, например старое от предыдущей операции.


 
Тимохов ©   (2004-05-27 17:30) [18]


> Ega23 ©   (27.05.04 17:20) [13]

точно.
т.е. если в таком коде
o := tmyobject.create();
в конструкторе класса tmyobject.create была ошибка, то o не изменит свое значение.

делать надо так, как говорит Анатолий: т.е. до try/

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


 
Shkut ©   (2004-05-27 17:31) [19]

Не ссорьтесь, Делфи 7 на подобного рода код выдет предупреждение, и пока конструктор не будет выненсен за пределы try, компилятор будет "варнякать".


 
Тимохов ©   (2004-05-27 17:32) [20]


> Ega23 ©   (27.05.04 17:28) [16]
> АП нас рассудит!  :о)

вы, похоже, про дургое говорите.


> Только выход из процедуры (функции) память освободит?


вот это очем?


 
Anatoly Podgoretsky ©   (2004-05-27 17:35) [21]

Shkut ©   (27.05.04 17:31) [19]
Это хорошо


 
Anatoly Podgoretsky ©   (2004-05-27 17:36) [22]

Ega23 ©   (27.05.04 17:28) [16]
Про что ты?


 
default ©   (2004-05-27 17:37) [23]

Shkut ©   (27.05.04 17:31) [19]
даже если переменная объекта глобальна?


 
Sandman25+1   (2004-05-27 17:38) [24]

>Только выход из процедуры (функции) память освободит?

Так память освобождается только для AnsiString, динамических массивов и интерфейсов.


 
Ega23 ©   (2004-05-27 17:41) [25]

вот это очем?

Так, ещё раз.
o:=TMyObject.Create;
Начал отрабатывать конструктор, начала заниматься память под о. Затем - сбой.
1. Чему будет равно о:
     а) Чему было равно до o:=TMyObject.Create;
     б) nil;
     в) Неизвестно
     г) Чему-то другому?
2. Освободиться ли та память, которая успела выделиться для о ДО сбоя?

Насчёт того, что o будет равно nil, я их всегда nil ПЕРЕД конструктором присваиваю. Так что, наверное, ответ на первый вопрос - а.
А вот со вторым мне не понятно.


 
Anatoly Podgoretsky ©   (2004-05-27 17:42) [26]

Sandman25+1   (27.05.04 17:38) [24]
Скажем так для субъектов с автоматическим контролем времени жизни, выше придены именно они.


 
Тимохов ©   (2004-05-27 17:44) [27]

1. - a
2. если сбой был в конструкторе, то успеет.

ЗЫ. Конструктор не выделяет память. Конструктор мало чем отличается от метода в тот момент, когда начал выполняться.
Отличия 2:
1. Исключение в конструкторе генерит удаление объекта (вызов деструктора и очистку памяти)
2. ключевое слово constructor заставляет компилитор сгенерить код отличный от вызова простого метода - сначала создается объект (NewInstance) затем вызов конструктора.


 
Тимохов ©   (2004-05-27 17:47) [28]

эх, спросил человек об одном а ему 27 постов лабуды какой-то.:)))

АВТОРУ.
Не думаю, что на ваш ответ без кода можно ответить.
SetLength вполне безопасен. Дело в вашем коде.


 
Sandman25+1   (2004-05-27 17:48) [29]

[26] Anatoly Podgoretsky ©   (27.05.04 17:42)

Каюсь, я преследовал личную цель - хотел убедиться, что меня не поправят и не добавят еще что-нибудь в этот список.


 
Anatoly Podgoretsky ©   (2004-05-27 17:49) [30]

Тимохов ©   (27.05.04 17:44) [27]
Не совсем соглащусь, для илюстрации два вызова, один как конструктор, а второй как метод

O := TO.Create
O.Create


 
Anatoly Podgoretsky ©   (2004-05-27 17:51) [31]

Sandman25+1   (27.05.04 17:48) [29]
Ну добился этого :-)


 
Тимохов ©   (2004-05-27 17:51) [32]


> Sandman25+1   (27.05.04 17:48) [29]
> Каюсь, я преследовал личную цель - хотел убедиться, что
> меня не поправят и не добавят еще что-нибудь в этот список.

:))
Я тоже не против покаятся - я когда видел ваш пост пожалел, что у меня нет карандаша - честное слово :))))


> Anatoly Podgoretsky ©   (27.05.04 17:49) [30]

Про второй случай я не говорил. Честно говоря даже не очень знаю, что будет - т.к. никогда так не делал.
Думаю что O.Create просто вызовет тело create как подпрограмму - никакого объект не создасться.


 
Anatoly Podgoretsky ©   (2004-05-27 17:54) [33]

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


 
Sandman25+1   (2004-05-27 17:55) [34]

[32] Тимохов ©   (27.05.04 17:51)

Не совсем понимаю. Вы хотели написать комментарий к моему посту? К какому именно?


 
Тимохов ©   (2004-05-27 17:55) [35]


> Anatoly Podgoretsky ©   (27.05.04 17:51) [31]

думаю понимаю, что вы говорите
вот это

"ключевое слово constructor заставляет компилитор сгенерить код отличный от вызова простого метода"

надо заменить на

"ключевое слово constructor в сочетании с именем класса заставляет компилитор сгенерить код отличный от вызова простого метода"


 
Ega23 ©   (2004-05-27 17:57) [36]

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

Это понятно. возможно я неточно выразился. я имел ввиду следующее:
TMyObj=class(TObject)
......
public
constructor Create;
destructor Destroy; override;
end;

constructor TMyObj.Create;
begin
inherited;
 ......
Какие-то другие действия
.......
end;

destructor TMyObj.Destroy;
begin
.....
Какие-то действия
.....
inherited;
end;

Сбой пришёл откуда-то из inherited конструктора. "Какие-то действия" - допустим, создание (удаление) каких-то других объектов.

Что будет в этом случае.


 
Тимохов ©   (2004-05-27 17:57) [37]


> Sandman25+1   (27.05.04 17:55) [34]

Мне показалось, что вы пошутили по поводу синего карандаша.
Похоже ошибся я - вы не шутили
Да я тоже пошутил - не берите в голову. :)))
Все нормально.

По поводу строк, дин мас и интерфейсов я бы добавил именно, то что добавли АП, т.к. это более широкое понятие.


 
Anatoly Podgoretsky ©   (2004-05-27 17:59) [38]

Тимохов ©   (27.05.04 17:55) [35]
Хорошая редакция. Борланд иногда исправляет терминологию в своих справках. Например виртуальный класс, теперь базовый класс


 
Sandman25+1   (2004-05-27 18:01) [39]

[37] Тимохов ©   (27.05.04 17:57)

Да, я ждал не синего карандаша. Мы же не в той конференции :)
Просто я не был уверен, что в Delphi 8 не добавили чего-нибудь новенького...


 
Тимохов ©   (2004-05-27 18:01) [40]


> Ega23 ©   (27.05.04 17:57) [36]

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

Например я всегда пишу.
destructor TMyObj.Destroy;
begin
  if kList <> nil then
  begin
     for I := 0 to kList.Count-1 do tobject(kList).free;
     kList.Free();
  end;
  inherited;
end;



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

Текущий архив: 2004.06.06;
Скачать: CL | DM;

Наверх




Память: 0.57 MB
Время: 0.034 c
1-1085628648
Настенька
2004-05-27 07:30
2004.06.06
Stringgrid


3-1084432467
SergeyI
2004-05-13 11:14
2004.06.06
Преобразовать _recordset в TMemoryStream


4-1083321796
Alex*
2004-04-30 14:43
2004.06.06
Как отловить перезагрузку или выключение Win2000


4-1083497908
anod
2004-05-02 15:38
2004.06.06
Изменить позицию пункта меню


4-1083324478
Unicode
2004-04-30 15:27
2004.06.06
Keyboard Layout