Объект Delphi без Create? [Дубликат]

Имейте в виду, что независимо от сценария причина всегда одинакова в .NET:

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

Вы либо никогда не присваивали какую-либо переменную, никогда не создавали экземпляр значения, присвоенного переменной, или вы вручную устанавливали переменную, равную Nothing / null, или вы вызывали функцию, которая установите для этой переменной значение Nothing / null.

9
задан Johan 24 May 2011 в 11:26
поделиться

4 ответа

Для использования обычного программирования ООП вы всегда должны использовать вид class. У вас будет самая мощная объектная модель в Delphi, включая интерфейс и дженерики (в более поздних версиях Delphi).

1. Записи, указатели и объекты

Записи могут быть злыми (медленная скрытая копия, если вы забыли объявить параметр как const, запишите скрытый код медленной очистки, fillchar заставить любую строку в записи стать утечкой памяти ...), но иногда очень удобно обращаться к двоичной структуре (например, к некоторому «маленькому значению») через указатель.

Динамический массив крошечных записи (например, с одним целым числом и одним двойным полем) будут намного быстрее, чем TList малых классов; с нашей TDynArray оберткой , вы получите доступ к записям высокого уровня, с сериализацией, сортировкой, хэшированием и т. д.

. Если вы используете указатели, вы должны знать, что вы делаем. Определенно предпочтительнее придерживаться классов и TPersistent, если вы хотите использовать магическую «модель владения компонентом VCL».

Наследование не допускается для записей. Вам нужно либо использовать «вариантную запись» (используя ключевое слово case в определении типа), либо использовать вложенные записи. При использовании C-like API вам иногда приходится использовать объектно-ориентированные структуры. Использование вложенных записей или вариантных записей ИМХО гораздо менее понятно, чем старая «натуральная» модель наследования.

2. Когда использовать объект

Но есть места, где объекты являются хорошим способом доступа к уже имеющимся данным.

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

В записи блога прошлым летом я разместил некоторые возможности для использования объектов:

  • Файл с отображением памяти, который я хотите быстро разобрать: указатель на такой объект просто велик, и у вас все еще есть методы; Я использую это для TFileHeader или TFileInfo, которые отображают заголовок .zip в SynZip.pas; ​​
  • Структура Win32, определенная вызовом API, в которой я помещал удобные методы для легкого доступа к данным ( для этого вы можете использовать запись, но если в структуре есть какая-то ориентация объектов, что очень часто, вам придется записывать записи, что не очень удобно);
  • Временная структура, определенная на стек, который использовался только во время процедуры: я использую это для TZStream в SynZip.pas или для наших связанных с RTTI классов, которые отображают Delphi сгенерированный RTTI объектно-ориентированным способом, а не как TypeInfo, который является ориентированным на функцию / процедуру. Сопоставляя непосредственно содержимое памяти RTTI, наш код быстрее, чем использование новых классов RTTI, созданных в куче. Мы не создаем никакой памяти, которая для платформы ORM, такой как наша, хороша для ее скорости. Нам нужно много информации RTTI, но нам нужно это быстро, нам нужно это прямо.

3. Как реализуется реализация объекта в современном Delphi

Тот факт, что объект нарушен в современном Delphi, является позором IMHO.

Обычно, если вы определяете запись в стеке, содержащую некоторые (например, строка), он будет инициализирован некоторым магическим кодом компилятора на начальном уровне метода / функции:

type TObj = object Int: integer; Str: string; end;
procedure Test;
var O: TObj
begin // here, an _InitializeRecord(@O,TypeInfo(TObj)) call is made
  O.Str := 'test';
  (...)
end;  // here, a _FinalizeRecord(@O,TypeInfo(TObj)) call is made

Те _InitializeRecord и _FinalizeRecord будут " подготовьте "затем" освободите "переменную O.Str.

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

Просто создайте источник еще раз, и будет ...

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

Итак, вот как выглядит полученный код:

/// used to store and retrieve Words in a sorted array
// - is defined either as an object either as a record, due to a bug
// in Delphi 2010 compiler (at least): this structure is not initialized
// if defined as a record on the stack, but will be as an object
TSortedWordArray = {$ifdef UNICODE}record{$else}object{$endif}
public
  Values: TWordDynArray;
  Count: integer;
  /// add a value into the sorted array
  // - return the index of the new inserted value into the Values[] array
  // - return -(foundindex+1) if this value is already in the Values[] array
  function Add(aValue: Word): PtrInt;
  /// return the index if the supplied value in the Values[] array
  // - return -1 if not found
  function IndexOf(aValue: Word): PtrInt; {$ifdef HASINLINE}inline;{$endif}
end;

{$ifdef UNICODE}record{$else}object{$endif} ужасно ... но ошибка генерации кода не произошла, поскольку ..

Полученные изменения в исходном коде не огромны, но немного разочаровывают. Я узнал, что более старая версия IDE (например, Delphi 6/7) не может анализировать такое объявление, поэтому иерархия классов будет разбита в редакторе ...: (

Обратная совместимость должна включать регрессионные тесты Многие пользователи Delphi остаются в этом продукте из-за существующего кода. Ломающиеся функции очень проблематичны для будущего Delphi, ИМХО: если вам нужно переписать много кода, то вам не следует просто переключать проект на C # или Java?

12
ответ дан Arnaud Bouchez 25 August 2018 в 21:21
поделиться

Вернувшись в более старые версии Delphi, которые не поддерживали записи с помощью методов, тогда использование object было способом получить ваши объекты, выделенные в стеке. Очень редко это могло бы принести пользу производительности. В настоящее время record лучше. Единственная функция, отсутствующая в record, - это способность наследовать от другого record.

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

1
ответ дан David Heffernan 25 August 2018 в 21:21
поделиться

Object не был методом Delphi 1 для настройки объектов; это был недолговечный метод Turbo Pascal по настройке объектов, который был заменен моделью Delphi TObject в Delphi 1. Он поддерживался для обратной совместимости, но его следует избегать по нескольким причинам:

  1. Как вы уже отметили, оно нарушено в более поздних версиях. И AFAIK не планирует исправлять это.
  2. Это концептуальная неправильная объектная модель. Вся точка объектно-ориентированного программирования, единственное, что действительно отличает ее от процедурного программирования, - это подстановка Liskov (наследование и полиморфизм), а типы наследования и значения не смешиваются.
  3. Вы теряете поддержку много функций, требующих потомков TObject.
  4. Если вам действительно нужны типы значений, которые не нужно динамически распределять и инициализировать, вместо этого вы можете использовать записи. Вы не можете наследовать их, но вы не можете сделать это очень хорошо с object, чтобы не потерять ничего здесь.

Что касается остальной части вопроса, есть не так много преимуществ по скорости. Модель TObject довольно быстро, особенно если вы используете диспетчер памяти FastMM для ускорения создания и уничтожения объектов, и если ваши объекты содержат много полей, они могут быть даже быстрее, чем записи во многих случаях, re передается по ссылке и не нужно копировать для каждого вызова функции.

7
ответ дан Mason Wheeler 25 August 2018 в 21:21
поделиться

Если задан выбор между «быстрым и, возможно, сломанным» и «быстрым и правильным», всегда выбирайте последнее.

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

5
ответ дан Rob Kennedy 25 August 2018 в 21:21
поделиться
Другие вопросы по тегам:

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