Механизм выделения и освобождения памяти и подсчета ссылок прозрачен для программы. От разработчика требуется только не вмешиваться в его работу с помощью низкоуровневых операций с указателями, чтобы менеджер памяти не запутался.
В отличие от string
, тип WideString
не имеет счетчика ссылок, и любое присваивание переменных этого типа приводит к копированию строки. Это сделано в целях совместимости с системным типом BSTR
, использующимся в COM/DCOM и OLE.
Функции Windows API не поддерживают тип string
. Они работают со строками, оканчивающимися на #0
(string
неявно добавляется нулевой символ, не учитывающийся при подсчете числа символов. Это сделано для совместимости с нуль-терминированными строками. Однако эта совместимость ограничена.
Для работы с нуль-терминированными строками в Delphi обычно служит тип PChar
. Формально это указатель на один символ типа Char
, но подразумевается, что это только первый символ строки, и за ним следуют остальные символы. Где будут эти символы размещены и какими средствами для них будет выделена память, программист должен решить самостоятельно. Он же должен позаботиться о том, чтобы в конце цепочки символов стоял #0
.
Строку, на которую указывает PChar
, можно использовать везде, где требуется string — компилятор сам выполнит необходимые преобразования. Обратное неверно. Фактически, string
— это указатель на начало строки, завершающейся нулем, т. е. тот самый указатель, который требуется при работе с PChar
. Однако, как уже отмечалось, некорректные манипуляции с этим указателем могут привести к нежелательным эффектам, поэтому компилятор требует явного приведения переменных и выражений типа string
к PChar
. В свою очередь, программист должен ясно представлять, к каким последствиям это может привести.
Если посмотреть описание функций API, имеющих строковые параметры, в справке, можно заметить, что в некоторых случаях строковые параметры имеют тип LPCTSTR
(как, например, у функции SetWindowText
), а в некоторых — LPTSTR
(GetWindowText
). Ранее мы говорили, что появление префикса LPCTSTR
имеют те строковые параметры, содержимое которых функция только читает, но не модифицирует. С такими параметрами работать проще всего. Рассмотрим на примере функции SetWindowText
, как можно работать с такими параметрами (листинг 1.20).
LPCTSTR
{ Вариант 1 }
SetWindowText(Handle, 'Строка');
{ Вариант 2
S — переменная типа string }
SetWindowText(PChar(S));
{ Вариант 3
X — переменная типа Integer }
SetWindowText(PChar('Выполнено ' + IntToStr(X) + '%'));
В первом варианте компилятор размещает строковый литерал в сегменте кода, а в функцию передает указатель на эту область памяти. Поскольку функция не модифицирует строку, а только читает, передача такого указателя не вызывает проблем.
Во втором варианте функции передается указатель, хранящийся в переменной S
. Такое приведение string
к PChar
безопасно, т. к. строка, на которую ссылается переменная S
, не будет модифицироваться. Но здесь существует одна тонкость: конструкция PChar(S)
— это не просто приведение типов, при ее использовании неявно вызывается функция _LStrToPChar
. Как мы уже говорили, когда string
хранит пустую строку, указатель просто имеет значение
nil. Функция _LStrToPChar
проверяет, пустая ли строка хранится в переменной, и, если не пустая, возвращает этот указатель, а если пустая, то возвращает не nil
, а указатель на символ #0
, который специально для этого размещен в сегменте кода. Поэтому даже если содержит пустую строку, в функцию будет передан ненулевой указатель.