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

С данными, предоставленными ФП, график, составленный

ggplot(mydata) + 
  aes(x = year, y = value, fill = coralType) +
  geom_col() + 
  facet_grid(coralType ~ location)

enter image description here

выглядит хорошо, потому что value является числовым .

Обратите внимание, что эстетика fill используется вместо эстетики colour. Кроме того, geom_col() используется в качестве ярлыка для geom_bar(stat = "identity").


Я могу воспроизвести проблему при изображении value как символа (который превращается в фактор с помощью ggplot2):

max_value <- 60
ggplot(mydata) + 
  aes(x = year, y = sprintf("%.2f %%", 100 * value / max_value),
      fill = coralType) +
  geom_col() + 
  facet_grid(coralType ~ location)

enter image description here [ 1113]

Это не похоже на суматоху, как на скриншоте OP из-за ограниченного количества точек данных.


Если OP хочет показывать проценты по оси Y вместо абсолютных значений, scale_y_continuous(labels = scales::percent) можно использовать с числовыми значениями:

max_value <- 60
ggplot(mydata) + 
  aes(x = year, y = value / max_value, fill = coralType) +
  geom_col() + 
  facet_grid(coralType ~ location) + 
  scale_y_continuous(labels = scales::percent)

enter image description here [1118 ]

Данные

mydata <-
structure(list(location = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L), .Label = c("site01", "site02", 
"site03", "site04", "site05", "site06", "site07", "site08"), class = "factor"), 
    coralType = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 
    2L, 2L, 2L, 2L, 2L, 2L), .Label = c("blue corals", "hard corals", 
    "sea fans", "sea pens", "soft corals"), class = "factor"), 
    longitude = c(143.515, 143.515, 143.515, 143.515, 143.515, 
    143.515, 143.515, 143.515, 143.515, 143.515, 143.515, 143.515, 
    143.515, 143.515, 143.515), latitude = c(-11.843, -11.843, 
    -11.843, -11.843, -11.843, -11.843, -11.843, -11.843, -11.843, 
    -11.843, -11.843, -11.843, -11.843, -11.843, -11.843), year = c(2010L, 
    2011L, 2012L, 2013L, 2014L, 2015L, 2016L, 2017L, 2011L, 2012L, 
    2013L, 2014L, 2015L, 2016L, 2017L), value = c(30, 30, 41, 
    43, 50, 54, 57, 58, 10, 11, 30, 31, 31, 32, 34)), row.names = c(NA, 
15L), class = "data.frame")

6
задан mghie 27 February 2009 в 17:20
поделиться

4 ответа

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

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

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

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

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

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

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

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

unit X;

interface

var
  MyVar: Integer;

Глобальная переменная определяется в datasegment. datasegment фиксируется. И в течение времени жизни программы эти переменные доступны. Что означает, что память не может использоваться для другого использования.

Локальные переменные:

procedure Test;
var
  MyVar: Integer;

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

Переменные "кучи":

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;

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

30
ответ дан 8 December 2019 в 02:12
поделиться

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

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

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

Рассмотрите растровый тип данных 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, который является в основном дружественной оберткой вокруг указателя на блок памяти.

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

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

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

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

not-so-nice свойство строк - то, что они считаются на ссылку. Каждая операция, которая могла возможно изменить строку, должна удостовериться, что подсчет ссылок равняется 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 (), когда адрес первого символа строки будет присвоен символьному указателю.

10
ответ дан 8 December 2019 в 02:12
поделиться

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

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

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

2
ответ дан 8 December 2019 в 02:12
поделиться

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

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

2
ответ дан 8 December 2019 в 02:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: