наследование объектов с дин. массивом - как?

Вопросы программирования на Free Pascal, использования компилятора и утилит.

Модератор: Модераторы

Сообщение alexs » 24.03.2008 20:18:44

Vadim
Переменную в стеке (локальная для процедуры) никто тебе никогда не обещал обнулять - ибо нафиг эти тормоза. Тебе нужно - буть добр инициализируй коректно.
Это из тойже оперы что и "НАСЛЕДИЛ - УБЕРИ ЗА СОБОЙ". Буть более строг к своему коду - и всё будет работать.
Аватара пользователя
alexs
долгожитель
 
Сообщения: 4060
Зарегистрирован: 15.05.2005 23:17:07
Откуда: г.Ставрополь

Сообщение zub » 24.03.2008 21:26:44

alexs
>>Переменную в стеке (локальная для процедуры) никто тебе никогда не обещал обнулять - ибо нафиг эти тормоза.

это не относится к compaler magic типам (стринги, динамические массивы), компилятор обязан их инициализировать. но -
В объекте наследнике стринги и динамические массивы родителя не инициализируются. в delphi так, в fpc видими тоже
перед использованием их нужно самостоятельно инициализировать ченить типа:
в конструкторе:
pointer(x):=nil;
и далее как обычно
setlength(x)...
zub
долгожитель
 
Сообщения: 2887
Зарегистрирован: 14.11.2005 23:51:26

Сообщение SAK » 25.03.2008 21:05:08

Наверное более корректно Initialize(x)
SAK
постоялец
 
Сообщения: 158
Зарегистрирован: 18.02.2006 00:45:14
Откуда: Тим

Сообщение alexs » 25.03.2008 21:34:12

Код: Выделить всё
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
долгожитель
 
Сообщения: 4060
Зарегистрирован: 15.05.2005 23:17:07
Откуда: г.Ставрополь

Сообщение zub » 26.03.2008 00:04:51

alexs
это если наследовать от 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;

для произвольного конструктора надо самому инитить
zub
долгожитель
 
Сообщения: 2887
Зарегистрирован: 14.11.2005 23:51:26

Сообщение alexs » 26.03.2008 09:05:43

По требованиям TurboPascal-а (они же перешли в FreePascal) ты обязан обеспечить вызов TObject.Init из своих конструкторов - иначе ты сам себе злобный буратино.

Не нарушайте требований - и всё будет работать как ожидается.

А Борланд не зря рекомендовал отказываться от объектов старой нотации и переходить к класам - там такие вещи делаются автоматом.
Аватара пользователя
alexs
долгожитель
 
Сообщения: 4060
Зарегистрирован: 15.05.2005 23:17:07
Откуда: г.Ставрополь

Сообщение Vadim » 26.03.2008 09:32:35

alexs
Переменную в стеке (локальная для процедуры) никто тебе никогда не обещал обнулять...

А при чём тут обнуление? Она просто должна существовать, а программист уже может назначить ей определённое значение. В данном случае к переменной просто нет доступа - она не существует.
Vadim
долгожитель
 
Сообщения: 4112
Зарегистрирован: 05.10.2006 08:52:59
Откуда: Красноярск

Сообщение alexs » 26.03.2008 11:07:21

Vadim писал(а): В данном случае к переменной просто нет доступа - она не существует.

Тут ты не прав. Так как это экземпляр объекта (object) а не класса (class). И он объявлен статически. Т.е. место под все переменные уже выделено в стеке. Если бы его объявляли через указатель и делали динамическим - то тогда оно было бы в куче.
Ошибка возникает же в методе SetLength из-за того, что переменная динамического массива - это фактически указатель (pointer). Процедура SetLength проверяет что указатель, переданный ей не пустой (а он будет не пустым, так как в стеке, отведённом под переменные, мусор - никто его нулями не забивал) и считает что уже массив какойто был объявлен - надо просто прозвести изменение его размера. Но на самом то деле массива не существует, и из-за мусора в стеке, наш указатель указывает вовсе не туда - получаем ошибку обращения к памяти.

Но если мы перед вызовом SetLength сделаем Initialize() или хотябы V=nil - то этим самым мы обнулим указатель - и всё становится на свои места.
Но, ещё раз повторю, тут правильно и проще обеспечить вызов коснтруктора TObject.Init (прямо или косвенно через наследование). Именно он обеспечивает обнуление всех пермененных, объявленных в вашем объекте.
Аватара пользователя
alexs
долгожитель
 
Сообщения: 4060
Зарегистрирован: 15.05.2005 23:17:07
Откуда: г.Ставрополь

Сообщение zub » 26.03.2008 11:26:15

>>Не нарушайте требований - и всё будет работать как ожидается.
может в TP и были такие требования, сейяас можно ни от чего не наследоваться. темболее tobject добавляет vmt которая может мне и не нужна
>>Именно он обеспечивает обнуление всех пермененных, объявленных в вашем объекте
string и array of инициализируются/убиваются компилятором автоматически всегда, с object - compiler magic не работает, об этом нужно помнить и заботится самостоятельно. к TObject.Init это не имеет никакого отношения.

ps. кстати заботится нужно не только о s:=nil перед использованием, но и о s:='' при выходе из зоны видимости. наследование от tobject тут никак не поможет
zub
долгожитель
 
Сообщения: 2887
Зарегистрирован: 14.11.2005 23:51:26

Сообщение alexs » 26.03.2008 11:48:48

Я пытаюсь растолковать, что необходимо пользоваться TObject.Init - и избежите проблем. Использование полей и методов объекта без вызова его конструктора - это дурной тон.
Аватара пользователя
alexs
долгожитель
 
Сообщения: 4060
Зарегистрирован: 15.05.2005 23:17:07
Откуда: г.Ставрополь

Сообщение Vadim » 26.03.2008 13:21:43

alexs
Нет уж позвольте Вам не позволить. :)
Но если мы перед вызовом SetLength сделаем Initialize() или хотябы V=nil...

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

Выдвигаю встречное предложение - использовать классы, :) т.к. поведение TObject неоднозначное, как мы видим на примере.
Vadim
долгожитель
 
Сообщения: 4112
Зарегистрирован: 05.10.2006 08:52:59
Откуда: Красноярск

Сообщение alexs » 26.03.2008 13:42:22

Vadim писал(а):Второе не работает, а вот Initialize() как раз однозначно

Грешен - сам не проверил. Действительно не работает.

Vadim писал(а):Выдвигаю встречное предложение - использовать классы

Абсолютно согласен :lol:
Аватара пользователя
alexs
долгожитель
 
Сообщения: 4060
Зарегистрирован: 15.05.2005 23:17:07
Откуда: г.Ставрополь

Сообщение zub » 26.03.2008 21:00:50

>>Выдвигаю встречное предложение - использовать классы, т.к. поведение TObject неоднозначное, как мы видим на примере.
Классы конечно хорошо, но часто object удобнее и логичнее. и поведение у него вполне однозначное))

pointer(s):=nil со стрингами работает, с массивами не проверял, но помоему тоже должно. собственно разницы нет как инициализировать, главное незабыть это сделать
zub
долгожитель
 
Сообщения: 2887
Зарегистрирован: 14.11.2005 23:51:26

Сообщение Vadim » 27.03.2008 11:54:33

zub
pointer(s):=nil со стрингами работает, с массивами не проверял,...

А Вы проверьте, проверьте, не стесняйтесь... :) Проверка позволяет избежать множества конфузов. :)
Vadim
долгожитель
 
Сообщения: 4112
Зарегистрирован: 05.10.2006 08:52:59
Откуда: Красноярск

Сообщение Sergei I. Gorelkin » 27.03.2008 12:32:43

pointer(s) := nil не включает compiler magic, поэтому сработает всегда. Собственно, это и делает Initialize.
Просто s := nil в случае с дин. массивом - то же самое, что SetLength(s, 0). Если в s мусор, то оно рухнет.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1406
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Пред.След.

Вернуться в Free Pascal Compiler

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 2

Рейтинг@Mail.ru