А как тогда она хранит ссылочный тип? Разве что хранит ссылку?
+
В таком случае остался один вопрос - ссылка - это как указатель, число?
Да. Размер вроде зависит от системы. Если не ошибаюсь - на вин х32 она весит 32 бита, на х64 - 64 бита.
И ещё, не знаю как классы, но записи могут изменять размещение элементов, для оптимизации. К примеру:
type
r1=record
b1:integer;
b2:byte;
end;
begin
writeln(sizeof(r1));
end.
У меня это выводит 8. Потому что на моему процессору под переменную b2
удобнее выделить 4 байта. (Кстати, это можно поменять с StructLayoutAttribute)
А я то думаю, почему же моя программа со структурой Color
Type Color=Record
Public L: SByte;
Public A: SByte;
Public B: SByte;
End;
Начала работать быстрее и кушатт меньше памяти после замены двумерного массива структур на трёхмерный массив SByte.
Ссылка – это особый “плавающий” указатель, значение которого (адрес, на который он указывает) автоматически корректируется сборщиком мусора в процессе дефрагментации управляемой динамической памяти (compacting GC), чтобы он продолжал указывать на тот же объект после его фактического перемещения сборщиком мусора.
Обычный “жесткий” указатель (pointer), если ему присвоить адрес какого-нибудь объекта в управляемой куче, в какой-то момент (после очередной сборки мусора и дефрагментации свободных участков кучи) просто “потеряет” свой объект и будет уже указывать неизвестно на что.
В исполняющей среде CLR (Common Language Runtime) существует 2 разных типа кучи: отдельно для малых объектов (SOH – Small Object Heap) и для больших (LOH – Large Object Heap). Чтобы не мешать друг другу, они располагаются в разных концах виртуальной памяти: первый – в нижних адресах, второй – в верхних адресах, и “растут” навстречу друг друг другу. В первом размещаются объекты размером менее 85 000 байт каждый, во втором от 85 000 байт и больше – в основном это длинные строки (т.е. ~ более 42000 символов в формате UTF-16) и длинные массивы (это особый случай – если более 1000 элементов любого типа).
При этом только первый тип кучи (SOH) подлежит автоматической дефрагментации при сборке мусора. Второй тип кучи (LOH) можно дефрагментировать только принудительно и только при работе под .NET 4.5.1 и новее.
В куче второго типа (LOH) куски памяти, занимаемые уже “мертвыми” большими объектами, в результате обычной фоновой сборки мусора только помечаются как свободные в специальном списке, образуя неиспользуемые “дыры” в виртуальной памяти (что может быть особенно критично для некоторых 32-битных приложений, учитывая ограниченную доступную адресацию для всего процесса ~2GB).
Чтобы уменьшить эффект “утечки памяти” в LOH из-за фрагментации (или даже риск краха всей системы в результате возникновения глобального OutOfMemoryException), при выделении памяти под очередной большой объект аллокатор LOH сперва должен просмотреть весь список свободных участков на предмет наличия “дырки” нужного размера. В случае же с выделением памяти в SOH аллокатору достаточно просто увеличить адрес указателя конца кучи.
Из-за этого выделение памяти в SOH обычно гораздо быстрее, чем в LOH (хотя это сильно зависит от реальной картины фрагментации LOH). Таким образом, LOH оптимизирована для хранения больших и долгоиграющих объектов, а SOH – небольших и с коротким временем жизни.
Summary
TL;DR Только для настоящих сварщиков! Если очень нужно, то есть интересная альтернатива использованию стандартной большой кучи в CLR. Не уверен, правда, что эти библиотеки сгодятся для нашего Паскаля – слишком уж низкоуровневые вещи они затрагивают. Но сама статья о проблемах управляемой памяти зачетная!
Прекрасная статья!
Они написаны на C#, поэтому их не только подключить, а ещё и переписать на паскаль можно. Собственно автор так и сказал, это только пример нескольких типов, ожидается что программист реализует остальные которые ему понадобятся.
Почему не работает такая программа?
function mySum<T>(a,b: T) := a + b;
begin
Println(mySum(2, 3))
end.
Как бы догадываюсь, что дело в типе параметров и не ко всем типам применимы разные операции и операторы, но хотелось бы заранее знать в каких случаях в обобщенных функциях можно применять тип T, а когда нужно указывать реальный тип параметров и выходной тип?
Ну, конечно. Разве у произвольного типа T есть +?
Тогда это только для обобщенных функций поиска с операциями сравнения можно так делать?
чего чего?
Обобщенные функции поиска?
И при чём тут операции сравнения?
Ну сравнение оно для всех же типов? А вот, например, если искать в каком-нибудь массиве какой-нибудь элемент нужно же поочередно их сравнивать, поэтому в таком случае для массива элементов из типа Т это подойдет. Ну сортировка там тоже. Где еще, я не знаю. Возможно где угодно, только чтобы к этим параметрам более не применялись никакие другие операции кроме сравнения?
Не совсем. operator>
и operator<
не работают для шаблонных типов. В сортировке сделано как то так:
function my_min<T>(o1,o2:T):T;
where T: System.IComparable;
begin
Result := o1.CompareTo(o2)<0 ? o1 : o2;
end;
begin
var m1 := my_min(2,3); //нормально, потому что integer реализует IComparable
var m2 := my_min(Arr(0),Arr(1)); //нет, потому что массивы не реализуют IComparable
end.
Только не в PaccalABC.NET ! Попробуйте написать обычную операцию отношения в обобщенном типа и увидите, куда пошлет Вас компилятор.
Те классы которые пишете вы - тоже могут реализовывать IComparable
, для этого надо написать так:
t1=class(System.IComparable)
end;
Конечно, это вызовет ошибку, потому что как компилятор узнает - что именно сравнивать в типе t1
. Чтоб узнать что надо реализовать - зажимаем Ctrl
и нажимаем на IComparable
:
type
/// Определяет обобщенный метод сравнения для типа, который реализуется типом или классом значения, чтобы упорядочить или сортировать его экземпляры.
IComparable = interface
{$region Методы}
/// Сравнивает текущий экземпляр с другим объектом того же типа и возвращает целое число, которое показывает, расположен ли текущий экземпляр перед, после или на той же позиции в порядке сортировки, что и другой объект.
/// Возвращаемое значение: Значение, указывающее, каков относительный порядок сравниваемых объектов.Возвращаемые значения представляют следующие результаты сравнения.Значение Описание Меньше нуля Этот экземпляр меньше параметра
public function CompareTo(obj: System.Object): integer; virtual;
{$endregion}
Ага, значит у этого интерфейса есть только 1 функция - CompareTo
. Вот её надо реализовать, чтоб работало. Копируем описание CompareTo
, прямо всю строчку, в наш класс, но убираем virtual
:
function my_min<T>(o1,o2:T):T;
where T: System.IComparable;
begin
Result := o2.CompareTo(o1)>0 ? o1 : o2;
end;
type
t1=class(System.IComparable)
b:byte;
public function CompareTo(obj: System.Object): integer;
begin
var a2 := obj as t1;
Result := self.b - a2.b;//self можно не писать. Это лишь чтоб обозначит явно, что "b" берём из данного объекта
end;
constructor(b:byte) :=
self.b := b;
end;
begin
var m1 := my_min(2,3);
writeln(m1);
var m2 := my_min(new t1(4),new t1(5));
writeln(m2.b);
end.
Это выводит 2
, а затем 4
.
Что тут надо исправить чтобы изменить исходную последовательность? почему так не меняется?
begin
var s := Seq(1, 2, 3); s.Println;
s.ForEach(x -> begin if x = 2 then x := 5; Print(x) end);
Println;
s.Println;//1,2,3
end.
begin
var s := Seq(1, 2, 3); s.Println;
s := s.Select(x -> x = 2 ? 5 : x); s.Println;
end.
А не меняется “так” потому, что у Вас нет операции присваивания для s.
.ForEach
передаёт в вашу лямбду только копию x
, потому что integer
это value
-тип. @RAlex правильно написал, можно через Select
. А ещё будет работать если элементы последовательности будут иметь ссылочный тип:
type
c1=class
b:byte;
constructor(b:byte) :=
self.b := b;
end;
r1=record
b:byte;
constructor(b:byte) :=
self.b := b;
end;
begin
var sc := Seq(new c1(2));
var sr := Seq(new r1(2));
sc.ForEach(procedure(o)->o.b := 3); //Сработает, потому что c1, как любой класс - ссылочный тип
sc.ForEach(procedure(o)->o := new c1(4)); //Не сработает потому что тут мы заменяем ссылку, а не меняем то что находится по этой ссылке
sr.ForEach(procedure(o)->o.b := 3); //Не сработает, потому что r1, как любая запись - value тип
sc.Select(o->o.b).Println;
sr.Select(o->o.b).Println;
end.
Что делает ключевое слово match
?
Это оператор сопоставления:
begin
var o: object := 5;
match o with
real(var r): writeln($'real({r})');
integer(var i): writeln($'integer({i})');
object(var o2): writeln($'object({o2})');
end;
end.