Использование указателей в Delphi

Я разрабатываю уже некоторое время, и до сих пор я не использовал указатели в своем развитии.

Итак, каковы преимущества указателей? Работает ли приложение быстрее или использует меньше ресурсов?

поскольку я уверен, что указатели важны, можете ли вы "указать" мне на некоторые статьи, Основные, но хорошие, чтобы начать использовать указатели в Delphi? Google дает мне слишком много, слишком особых результатов.

4 ответов


указатель-это переменная, указывающая на часть памяти. Преимущества являются следующими:

  • вы можете дать этот кусок памяти нужного размера.
  • вам нужно только изменить указатель, чтобы указать на другой кусок памяти, который экономит много времени копирования.

Delphi использует много скрытых указателей. Например, если вы используете:

var
  myClass : TMyClass;
begin
  myClass := TMyClass.Create;

myClass является указателем на объект.

Другим примером является динамический массив. Это тоже указатель.

чтобы понять больше о указателях, вам нужно больше понять о памяти. Каждая часть данных может существовать в разных частях данных.

глобальные переменные:

unit X;

interface

var
  MyVar: Integer;

глобальные переменные, определенные в данных сегмент. Сегмент данных исправлен. И в течение жизни программы эти переменные доступны. Это означает, что память не может использоваться для других целей.

местные переменные:

procedure Test;
var
  MyVar: Integer;

локальная переменная существует в стеке. Это часть памяти, которая используется для ведения домашнего хозяйства. Он содержит параметры для функции (ok некоторые из них помещены в регистр, но теперь это не важно). Он содержит обратный адрес, поэтому процессор знает, куда возвращаться, если программа закончилась. И содержит локальные переменные, используемые в функции. Локальные переменные существуют только в течение времени существования функции. Если функция завершена, вы не можете получить доступ к локальная переменная надежным способом.

переменные кучи:

procedure Test2;
var
  MyClass: TMyClass;
begin
  MyClass := TMyClass.Create;

переменная MyClass является указателем (который является локальной переменной, определенной в стеке). При построении объекта вы выделяете часть памяти в куче (большую часть "другой" памяти, которая не используется для программ и стеков). Переменная MyClass содержит адрес этого фрагмента памяти. Переменные кучи существуют до их освобождения. Это означает, что если вы выходите из funcion Test2 без освобождая объект, объект все еще существует в куче. Но вы не сможете получить к нему доступ, потому что адрес (переменная MyClass) исчез.

лучшие практики

почти всегда предпочтительно выделять и освобождать переменную указателя на одном уровне.

например:

var
  myClass: TMyClass;
begin
  myClass := TMyClass.Create;
  try
    DoSomething(myClass);
    DoSomeOtherthing(myClass);
  finally
    myClass.Free;
  end;
end;

если можно, попробуйте избежать функций, возвращающих экземпляр объекта. Он никогда не уверен, что вызывающий должен избавиться от объект. И это создает утечки памяти или сбои.


вам было дано много хороших ответов до сих пор, но начиная с ответа, что вы уже имеете дело с указателями при использовании длинных строк, динамических массивов и ссылок на объекты, вы должны начать задаваться вопросом, почему вы б используйте указатели вместо длинных строк, динамических массивов и ссылок на объекты. Есть ли причина все еще использовать указатели, учитывая, что Delphi хорошо скрывает их от вас во многих случаях?

позвольте мне привести вам два примера использование указателя в Delphi. Вы увидите, что это, вероятно, совсем не актуально для вас, если вы в основном пишете бизнес-приложения. Однако это может стать важным, если вам когда-либо понадобится использовать функции Windows или сторонних API, которые не импортируются ни одним из стандартных блоков Delphi и для которых не могут быть найдены единицы импорта в (например) библиотеках JEDI. И это может быть ключом к достижению необходимого последнего бита скорости в коде обработки строк.

указатели можно использовать для работа с типами данных различных размеров (неизвестных во время компиляции)

рассмотрим тип данных растрового изображения Windows. Каждое изображение может иметь разную ширину и высоту, и есть разные форматы, начиная от черно-белый (1 бит на пиксель) за 2^4, 2^8, 2^16, 2^24 или даже 2^32 значений серого цвета. Это означает, что во время компиляции неизвестно, сколько памяти будет занимать растровое изображение.

в windows.pas есть TBitmapInfo тип:

type
  PBitmapInfo = ^TBitmapInfo;
  tagBITMAPINFO = packed record
    bmiHeader: TBitmapInfoHeader;
    bmiColors: array[0..0] of TRGBQuad;
  end;
  TBitmapInfo = tagBITMAPINFO;

на TRGBQuad элемент описывает один пиксель, но растровое изображение, конечно, содержит более одного пикселя. Поэтому никогда не следует использовать локальную переменную типа TBitmapInfo, но всегда указатель на него:

var
  BmpInfo: PBitmapInfo;
begin
  // some other code determines width and height...
  ...
  BmpInfo := AllocMem(SizeOf(TBitmapInfoHeader)
    + BmpWidth * BmpHeight * SizeOf(TRGBQuad));
  ...
end;

теперь с помощью указателя можно получить доступ ко всем пикселям, хотя TBitmapInfo имеет только один. Обратите внимание, что для такого кода Вы должны отключить проверку диапазона.

прочее как это, конечно, также может быть обработано с помощью TMemoryStream class, который в основном является дружественной оболочкой вокруг указателя на блок памяти.

и, конечно, гораздо проще просто создать TBitmap и назначить его ширину, высоту и формат пикселей. Чтобы заявить это снова, Delphi VCL устраняет большинство случаев, когда указатели в противном случае были бы необходимы.

указатели на символы могут использоваться для ускорения строки операции

это, как и большинство микро-оптимизаций, что-то, что можно использовать только в крайних случаях, после того, как вы профилировали и нашли код, используя строки, чтобы потреблять много времени.

хорошим свойством строк является то, что они подсчитываются по ссылкам. Копирование их не копирует память, которую они занимают, а только увеличивает количество ссылок. Только когда код пытается изменить строку, которая имеет счетчик ссылок больше 1, память будет скопирована, чтобы создайте строку с числом ссылок 1, которое затем можно безопасно изменить.

не очень хорошее свойство строк заключается в том, что они подсчитываются по ссылкам. Каждая операция, которая может изменить строку, должна убедиться, что счетчик ссылок равен 1, потому что в противном случае изменения строки были бы опасны. Замена символа в строке является такой модификацией. Чтобы убедиться, что счетчик ссылок равен 1 вызов UniqueString () добавляется компилятор всякий раз, когда символ в строке записывается. Теперь пишу n символы строки в цикле будет вызывать UniqueString () называли n раз, даже если после того, как первый раз уверен, что счетчик ссылок равен 1. Это означает, в основном n-1 звонки UniqueString () выполняются без надобности.

использование указателя на символы является распространенным способом ускорения строковых операций, которые задействовать петли. Представьте, что вы хотите (для целей отображения) заменить все пробелы в строке маленькой точкой. Используйте представление CPU отладчика и сравните код, выполненный для этого кода

procedure MakeSpacesVisible(const AValue: AnsiString): AnsiString;
var
  i: integer;
begin
  Result := AValue;
  for i := 1 to Length(Result) do begin
    if Result[i] = ' ' then
      Result[i] := $B7;
  end;
end;

этот код

procedure MakeSpacesVisible(const AValue: AnsiString): AnsiString;
var
  P: PAnsiChar;
begin
  Result := AValue;
  P := PAnsiChar(Result);
  while P[0] <> #0 do begin
    if P[0] = ' ' then
      P[0] := $B7;
    Inc(P);
  end;
end;

во второй функции будет только один вызов UniqueString (), когда адрес первого строкового символа присваивается указателю char.


вы, вероятно, использовали указатели, но вы просто не знаете его. Переменная класса-указатель, строка-указатель, динамический массив-указатель, Delphi просто скрывает его для вас. Вы увидите их при выполнении вызовов API (приведение строк в PChar), но даже тогда Delphi может многое скрыть.

см. ответ Gamecats для преимуществ указателей.

в этом About.com статья вы можете найти основное объяснение указателей в Delphi.


указатели необходимы для некоторых структур данных. Самый простой пример-связанный список. Преимущество таких структур в том, что можно рекомбинировать элементы, не перемещая их в памяти. Например, вы можете иметь связанный список больших сложных объектов и быстро менять местами любые два из них, потому что вам действительно нужно настроить два указателя вместо перемещения этих объектов.

Это относится ко многим языкам, включая Object Pascal (Delphi).