наследование объектов с дин. массивом - как?
Модератор: Модераторы
alexs
>>Переменную в стеке (локальная для процедуры) никто тебе никогда не обещал обнулять - ибо нафиг эти тормоза.
это не относится к compaler magic типам (стринги, динамические массивы), компилятор обязан их инициализировать. но -
В объекте наследнике стринги и динамические массивы родителя не инициализируются. в delphi так, в fpc видими тоже
перед использованием их нужно самостоятельно инициализировать ченить типа:
в конструкторе:
pointer(x):=nil;
и далее как обычно
setlength(x)...
>>Переменную в стеке (локальная для процедуры) никто тебе никогда не обещал обнулять - ибо нафиг эти тормоза.
это не относится к compaler magic типам (стринги, динамические массивы), компилятор обязан их инициализировать. но -
В объекте наследнике стринги и динамические массивы родителя не инициализируются. в delphi так, в fpc видими тоже
перед использованием их нужно самостоятельно инициализировать ченить типа:
в конструкторе:
pointer(x):=nil;
и далее как обычно
setlength(x)...
- alexs
- долгожитель
- Сообщения: 4066
- Зарегистрирован: 15.05.2005 23:17:07
- Откуда: г.Ставрополь
- Контактная информация:
Код: Выделить всё
program project1;
{$mode objfpc}{$H+}
uses Objects;
type
TTestArr = array of integer;
TDemo = object(TObject)
V:TTestArr;
end;
{ TDemo2 }
TDemo2 = object(TDemo)
procedure DoTest;
end;
{ TDemo2 }
procedure TDemo2.DoTest;
begin
SetLength(V, 10);
end;
procedure Inits;
var
D:TDemo2;
begin
D.Init;
D.DoTest;
D.Done;
end;
begin
writeln('aaaaaa');
Inits;
end.
Вот образец - если коментировать вызов конструктора D.Init в процедуре Inits то нарушение доступа есть. Если контруктор как правильно вызывать - всё работает.
Вызов writeln('aaaaaa'); в начале примера необходим чтобы стек стал "грязным" - без него стек заполнен нулями, и создаётся видимость что код работает верно.
Не важно, от куда происходит компиляция - хоть лазарь, хоть чистый fpc, результат стабилен.
alexs
это если наследовать от tobject
вот из rtl:
для произвольного конструктора надо самому инитить
это если наследовать от tobject
вот из rtl:
Код: Выделить всё
CONSTRUCTOR TObject.Init;
VAR LinkSize: LongInt; Dummy: DummyObject;
BEGIN
LinkSize := PtrInt(@Dummy.Data)-PtrInt(@Dummy); { Calc VMT link size }
FillChar(Pointer(PtrInt(@Self)+LinkSize)^,
SizeOf(Self)-LinkSize, #0); { Clear data fields }
END;для произвольного конструктора надо самому инитить
- alexs
- долгожитель
- Сообщения: 4066
- Зарегистрирован: 15.05.2005 23:17:07
- Откуда: г.Ставрополь
- Контактная информация:
По требованиям TurboPascal-а (они же перешли в FreePascal) ты обязан обеспечить вызов TObject.Init из своих конструкторов - иначе ты сам себе злобный буратино.
Не нарушайте требований - и всё будет работать как ожидается.
А Борланд не зря рекомендовал отказываться от объектов старой нотации и переходить к класам - там такие вещи делаются автоматом.
Не нарушайте требований - и всё будет работать как ожидается.
А Борланд не зря рекомендовал отказываться от объектов старой нотации и переходить к класам - там такие вещи делаются автоматом.
- alexs
- долгожитель
- Сообщения: 4066
- Зарегистрирован: 15.05.2005 23:17:07
- Откуда: г.Ставрополь
- Контактная информация:
Vadim писал(а): В данном случае к переменной просто нет доступа - она не существует.
Тут ты не прав. Так как это экземпляр объекта (object) а не класса (class). И он объявлен статически. Т.е. место под все переменные уже выделено в стеке. Если бы его объявляли через указатель и делали динамическим - то тогда оно было бы в куче.
Ошибка возникает же в методе SetLength из-за того, что переменная динамического массива - это фактически указатель (pointer). Процедура SetLength проверяет что указатель, переданный ей не пустой (а он будет не пустым, так как в стеке, отведённом под переменные, мусор - никто его нулями не забивал) и считает что уже массив какойто был объявлен - надо просто прозвести изменение его размера. Но на самом то деле массива не существует, и из-за мусора в стеке, наш указатель указывает вовсе не туда - получаем ошибку обращения к памяти.
Но если мы перед вызовом SetLength сделаем Initialize() или хотябы V=nil - то этим самым мы обнулим указатель - и всё становится на свои места.
Но, ещё раз повторю, тут правильно и проще обеспечить вызов коснтруктора TObject.Init (прямо или косвенно через наследование). Именно он обеспечивает обнуление всех пермененных, объявленных в вашем объекте.
>>Не нарушайте требований - и всё будет работать как ожидается.
может в TP и были такие требования, сейяас можно ни от чего не наследоваться. темболее tobject добавляет vmt которая может мне и не нужна
>>Именно он обеспечивает обнуление всех пермененных, объявленных в вашем объекте
string и array of инициализируются/убиваются компилятором автоматически всегда, с object - compiler magic не работает, об этом нужно помнить и заботится самостоятельно. к TObject.Init это не имеет никакого отношения.
ps. кстати заботится нужно не только о s:=nil перед использованием, но и о s:='' при выходе из зоны видимости. наследование от tobject тут никак не поможет
может в TP и были такие требования, сейяас можно ни от чего не наследоваться. темболее tobject добавляет vmt которая может мне и не нужна
>>Именно он обеспечивает обнуление всех пермененных, объявленных в вашем объекте
string и array of инициализируются/убиваются компилятором автоматически всегда, с object - compiler magic не работает, об этом нужно помнить и заботится самостоятельно. к TObject.Init это не имеет никакого отношения.
ps. кстати заботится нужно не только о s:=nil перед использованием, но и о s:='' при выходе из зоны видимости. наследование от tobject тут никак не поможет
alexs
Нет уж позвольте Вам не позволить.
Второе не работает, а вот Initialize() как раз однозначно свидетельствует о том, что переменной нет, не выделена память, хотя и должна была быть.
Я ведь о чём и говорю, что нет этой переменной.
Выдвигаю встречное предложение - использовать классы,
т.к. поведение TObject неоднозначное, как мы видим на примере.
Нет уж позвольте Вам не позволить.
Но если мы перед вызовом SetLength сделаем Initialize() или хотябы V=nil...
Второе не работает, а вот Initialize() как раз однозначно свидетельствует о том, что переменной нет, не выделена память, хотя и должна была быть.
Я ведь о чём и говорю, что нет этой переменной.
Я пытаюсь растолковать, что необходимо пользоваться TObject.Init - и избежите проблем.
Выдвигаю встречное предложение - использовать классы,
>>Выдвигаю встречное предложение - использовать классы, т.к. поведение TObject неоднозначное, как мы видим на примере.
Классы конечно хорошо, но часто object удобнее и логичнее. и поведение у него вполне однозначное))
pointer(s):=nil со стрингами работает, с массивами не проверял, но помоему тоже должно. собственно разницы нет как инициализировать, главное незабыть это сделать
Классы конечно хорошо, но часто object удобнее и логичнее. и поведение у него вполне однозначное))
pointer(s):=nil со стрингами работает, с массивами не проверял, но помоему тоже должно. собственно разницы нет как инициализировать, главное незабыть это сделать
- Sergei I. Gorelkin
- энтузиаст
- Сообщения: 1409
- Зарегистрирован: 24.07.2005 14:40:41
- Откуда: Зеленоград
