Большинство типов в PascalABC.NET подразделяются на две большие группы: размерные и ссылочные.
К размерным относятся все простые типы и записи. Более точно, все размерные типы наследуются от .NET-типа System.ValueType.
К ссылочным типам относятся строки, динамические массивы, кортежи, классы, последовательности и процедурный тип. Более точно, все ссылочные типы наследуются от .NET-типа System.Object и не наследуются от System.ValueType.
Размерные и ссылочные типы отличаются следующими характеристиками:
Кроме того, в PascalABC.NET имеется несколько типов, унаследованных от Delphi Object Pascal, которые трудно отнести к размерному или ссылочному типу. Это статические массивы, множества, размерные строки и файлы. По представлению в памяти они относятся к ссылочному типу, но по поведению (присваивание, сравнение, передача в подпрограммы) - к размерному.
Размерные типы более эффективны при вычислениях: они занимают меньше памяти и операции, выполняемые над небольшими размерными типами, максимально эффективны. Ссылочные типы обладают большей гибкостью: память под них выделяется динамически в процессе работы программы и освобождается автоматически когда объект ссылочного типа перестаёт использоваться.
Память под переменную размерного типа выделяется на программном стеке. Для глобальных переменных память выделяется при запуске программы, для локальных - в момент вызова подпрограммы. При этом переменная размерного типа хранит значение этого типа.
var i: integer; // под i выделяется память на программном стеке
i := 5;
Переменная ссылочного типа хранит ссылку на объект некоторого класса в динамической памяти. Если она не инициализирована, то хранит специальное значение nil (нулевая ссылка). Для инициализации ссылочных переменных используется вызов конструктора соответствующего класса:
type Person = auto class
name: string;
age: integer;
end;
var p: Person; // p хранит значение nil, память под объект не выделена
p := new Person('Иванов',20); // конструктор выделяет память под объект и записывает ссылку на него в переменную p
Строки в PascalABC.NET инициализируются по умолчанию не значением nil, а пустой строкой.
При присваивании переменных размерного типа копируется значение этого типа.
type Point3 = record
x,y,z: real;
end;
var p1,p2: Point3;
p1.x := 1; p1.y := 2; p1.z := 3;
p2 := p1; // копируются все поля
Print(p2); // (1,2,3)
p1.x := 4; p1.y := 5; p1.z := 6;
Print(p2); // (1,2,3) - p2 не меняется, т.к. занимает на стеке другую память
При присваивании переменных ссылочного типа копируется ссылка, в итоге после присваивания обе ссылки ссылаются на один объект в динамической памяти:
type Point3 = auto class
x,y,z: real;
end;
var p1,p2: Point3; // переменные хранят нулевую ссылку nil
p1 := new Point3(1,2,3);
p2 := p1; // копируется ссылка, после чего p2 ссылается на тот же объект, что и p1
Print(p2); // (1,2,3)
p1.x := 4; p1.y := 5; p1.z := 6;
Print(p2); // (4,5,6) - объект поменялся, p2 ссылается на тот же объект, что и p1
Статические массивы, множества и размерные строки при присваивании ведут себя как размерные типы. Так, при присваивании одного статического массива другому копируются все элементы:
var a,a1: array [1..1000000] of integer;
a1 := a; // копируются все 1000000 элементов (долго)
Сравнение на равенство и на неравенство объектов размерного типа сравнивает их значения. В частности, две переменные типа запись равны если равны все поля этих записей.
type PersonRec = record
name: string;
age: integer;
end;
var p,p1: PersonRec;
p.name := 'Иванов'; p.age := 20;
p1.name := 'Иванов'; p1.age := 20;
writeln(p1 = p); // True
Две переменные ссылочного типа по умолчанию равны если они ссылаются на один и тот же объект.
type Person = auto class
name: string;
age: integer;
end;
var p := new Person('Иванов',20);
var p1 := new Person('Иванов',20);
writeln(p1 = p); // False
Однако операцию сравнения можно перегрузить. Например, для строк и кортежей сравнение на равенство переопределено так, что сравниваются не ссылки, а значения.
При передаче размерных типов по значению происходит копирование значения фактического параметра в переменную-формальный параметр. Если размерный тип имеет большой размер, это может занимать продолжительное время, поэтому размерный тип в этом случае передаётся по ссылке на константу:
type Point3 = record
x,y,z: real;
end;
procedure PrintPoint(const p: Point3);
begin
Print(p.x,p.y,p.z)
end;
Ссылочные типы передаются в подпрограмму, как правило, по значению. При передаче таких параметров происходит копирование ссылки, в результате формальный и фактический параметр будут ссылаться на один объект.
procedure Change666(a: array of integer);
begin
a[0] := 666;
end;
При этом в результате изменения формального параметра внутри подпрограммы меняется и содержимое соответствующего фактического параметра при вызове подпрограммы.
Ссылочные типы передаются в подпрограмму по ссылке только в случае если сама ссылка меняется внутри подпрограммы:
procedure CreateA(var a: array of integer);
begin
a := new integer[10];
end;
Статические массивы, размерные строки и множества при передаче в подпрограммы ведут себя как размерные типы. Например, неэффективно пытаться передать в подпрограмму статический массив по значению, поскольку происходит копирование большого объёма данных. Поэтому статические массивы всегда передаются по ссылке:
type Arr = array [1..100] of integer;
procedure PrintArray(const a: Arr; n: integer);
begin
for var i:=1 to n do
Print(a[i])
end;
Размерные типы распределяются на программном стеке, поэтому не нуждаются в специальном управлении памятью. Под глобальные размерные переменные память распределена всё время работы программы. Под локальные размерные переменные память выделяется в момент вызова подпрограммы, а освобождается в момент завершения работы этой подпрограммы.
Управление памятью для ссылочных типов осуществляется автоматически сборщиком мусора. Сборщик мусора запускается в неопределенный момент времени когда управляемой памяти перестаёт хватать. Он возвращает в пул неиспользуемой памяти те объекты, на которые больше никто не ссылается, после чего дефрагментирует оставшуюся память, в результате чего динамическая память всегда дефрагментирована и ее выделение при вызове конструктора происходит практически мгновенно.
Статические массивы, размерные строки, множества и файлы с точки зрения распределения памяти относятся к ссылочным и память, занимаемая значениями этого типа, также управляется сборщиком мусора.