134 lines
9.7 KiB
HTML
134 lines
9.7 KiB
HTML
|
||
<html>
|
||
|
||
<head>
|
||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||
<style type="text/css">
|
||
.code_block {
|
||
background: #c0c0c0;
|
||
padding: 5px;
|
||
padding-right: 20px;
|
||
border: solid 1px black;
|
||
}
|
||
</style>
|
||
|
||
<script type="text/javascript">
|
||
function toggle_show(id) {
|
||
document.getElementById(id).style.display = document.getElementById(id).style.display == 'none' ? 'block' : 'none';
|
||
}
|
||
</script>
|
||
</head>
|
||
|
||
|
||
|
||
<body>
|
||
<font size="3"><a href="../Справка.html"><=</a></font><br>
|
||
<br>
|
||
<font size="5">Сохранение ссылочных типов в блочный файл.</font><br>
|
||
<hr />
|
||
|
||
|
||
|
||
<p onClick="toggle_show('decl')"><font size="4" color="008000" style="cursor: pointer">В чём проблема ссылочных типов</font></p>
|
||
<div id=decl style="display: none">
|
||
<p class=code_block>
|
||
<t style="margin-left: 00px">type </t>
|
||
<br><t style="margin-left: 10px">r1 = record </t>
|
||
<br><t style="margin-left: 20px">b: byte; </t>
|
||
<br><t style="margin-left: 20px">i: integer; </t>
|
||
<br><t style="margin-left: 20px">s: string; </t>
|
||
<br><t style="margin-left: 10px">end; </t>
|
||
<br><t style="margin-left: 00px"> </t>
|
||
<br><t style="margin-left: 00px">begin </t>
|
||
<br><t style="margin-left: 10px">var a: r1; </t>
|
||
<br><t style="margin-left: 10px">a.b := 5; </t>
|
||
<br><t style="margin-left: 10px">a.i := 12345; </t>
|
||
<br><t style="margin-left: 10px">a.s := 'abcdef'*10000;//Это 60 000 символов, ~120KB. А значит в записи никаким образом не поместилось бы </t>
|
||
<br><t style="margin-left: 00px">end. </t>
|
||
</p>
|
||
r1, byte и integer из этого примера - размерные типы (записи), а значит они хранят свои данные прямо в переменных.<br>
|
||
А string - ссылочный тип (класс). Это значит что в поле r1.s хранится только ссылка (особый указатель) на содержимое строки.<br>
|
||
<br>
|
||
То есть, в переменной "a" хранится три значения: byte, integer и ссылка.<br>
|
||
А содержимое строки хранится где-то в оперативной памяти, куда указывает эта ссылка.<br>
|
||
<br>
|
||
"BlockFileOf<T>" берёт содержимое переменных одним блоком памяти.<br>
|
||
Поэтому, если попытаться сохранить содержимое r1 в типизированный файл.<br>
|
||
(отключив защиту от дурака, запрещающую ссылочные типы)<br>
|
||
В файл сохранится значение ссылки, а не строки.<br>
|
||
<br>
|
||
Кроме того, ссылки отличаются от указателей тем, что они ссылаются на значение, которое может "плавать" в оперативной памяти.<br>
|
||
Точнее, сборщик мусора может двигать эти значения, для экономии места.<br>
|
||
Это значит что если сохранить запись с ссылкой в файл и сразу загрузить эту ссылку назад.<br>
|
||
Её значение может быть уже устаревшим.<br>
|
||
<hr />
|
||
</div>
|
||
|
||
|
||
|
||
<p onClick="toggle_show('io_basics')"><font size="4" color="008000" style="cursor: pointer">Размерные массивы и строки</font></p>
|
||
<div id=io_basics style="display: none">
|
||
|
||
Посмотрите в этот файл:<br>
|
||
C:\PABCWork.NET\Samples\BlockFileOfT\Сравнение скорости.pas<br>
|
||
Найдите там тип "ValueString255".<br>
|
||
На нём будут строиться дальнейшие обноснования:<br>
|
||
Этот тип представляет строку с фиксированной длинной, а точнее длиной в 255 символов.<br>
|
||
<br>
|
||
Первое, что видно - прямо перед объявлением типа стоит атрибут "StructLayout":<br>
|
||
"LayoutKind.Explicit" разрешает применять атрибут "FieldOffset" к полям.<br>
|
||
А "Size" позволяет явно указать сколько байт выделять под данную запись.<br>
|
||
Благодаря последнему - можно выделить место сразу под все 255 символов строки, не делая 255 полей с типом "char".<br>
|
||
<br>
|
||
Далее, в записи "ValueString255" есть всего 2 поля, "length" (длина) и "body" (тело, а точнее его начало)<br>
|
||
"length" это кол-во символов во всей строке. В данном случае не может быть больше 255 символов,<br>
|
||
поэтому достаточно 1 байта чтобы хранить любую возможную длину строки.<br>
|
||
А "body" это начало самогО содержимого строки.<br>
|
||
Вообще, поле "body" не так уж и нужно, но с ним будет проще, потому что если создать указатель на "body" -<br>
|
||
он сразу оказывается и указателем на содержимое строки, без каких-либо преобразований.<br>
|
||
<br>
|
||
Далее идут методы для преобразования между обычной строкой и нашей размерной.<br>
|
||
Я сделал их "operator explicit"-ами, потому что их вызов красивый и удобный.<br>
|
||
<br>
|
||
Смотрим в первый, преобразовывающий массив символов в размерную строку:<br>
|
||
<br>
|
||
Если выполнить "Result.length := a.Length" и передать массив длинной больше 255 символов -<br>
|
||
в этой строчке произойдёт переполнение и "Result.length" запонится мусором, поэтому сначала надо уменьшить значение до "MaxLength".<br>
|
||
<br>
|
||
Далее, если длина равна нулю - ничего больше делать не надо.<br>
|
||
В противном случае - используем процедуру "CopyMem".<br>
|
||
<br>
|
||
Насчёт того, как работает "CopyMem", можно написать целую диссертацию.<br>
|
||
Но главное, что надо знать вам: если попытаться получить указатель на элемент массива ("@a[n]") -<br>
|
||
получаем утечку памяти. Однако, если передать "a[n]" в "CopyMem" как var-параметр<br>
|
||
(что, по сути, тоже создаёт указатель на этот элемент массива),<br>
|
||
то всё в порядке.<br>
|
||
<br>
|
||
В данном случае мы копируем память начиная с нулевого элемента массива<br>
|
||
Вставляем её в "Result.body"<br>
|
||
И всего копируем "Result.length*2" байт (*2 потому что каждый символ занимает 2 байта в памяти)<br>
|
||
<br>
|
||
Преобразование из "string" в размерную строку работает также, там ничего интересного.<br>
|
||
У преобразования размерной строки в массив символов всего одна особенность:<br>
|
||
Теперь результат у нас ссылочный (массив), поэтому память под него нужно выделить вручную ("new char[...]").<br>
|
||
<br>
|
||
И последнее преобразование, из "ValueString255" в "string":<br>
|
||
А вот тут очень приятно вышло - у "string" есть конструктор который идеально подходит:<br>
|
||
Первый параметр это указатель на начало блока памяти содержащего символы. То есть "@s.body" и даже тип подходит без преобразований.<br>
|
||
Второй параметр - смещение начала. У нас символы начинаются прямо на "body", поэтому 0.<br>
|
||
И последний - количество символов.<br>
|
||
<br>
|
||
Последнее, что есть в данной записи - перегрузка "ToString". Это вообще не обязательный код,<br>
|
||
но с этой функцией размерная строка будет вести себя точно так же, как и обычная, если передать её во "Writeln".<br>
|
||
Также можно добавлять и другие свои методы в данный тип и даже сделать все те же методы, как и у ссылочных строк, или даже больше.<br>
|
||
<br>
|
||
Ну а размерные массивы ничем не отличаются в реализации.<br>
|
||
Можно даже сказать, что "ValueString255" это размерный массив символов, ничего от этого не изменится.<br>
|
||
<hr />
|
||
</div>
|
||
|
||
|
||
|
||
</body>
|
||
|
||
</html> |